0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018: package org.apache.lenya.cms.publication;
0019:
0020: import java.io.IOException;
0021: import java.io.InputStream;
0022: import java.io.OutputStream;
0023: import java.util.ArrayList;
0024: import java.util.Arrays;
0025: import java.util.Collections;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.util.Set;
0032:
0033: import org.apache.avalon.framework.context.Context;
0034: import org.apache.avalon.framework.context.ContextException;
0035: import org.apache.avalon.framework.context.Contextualizable;
0036: import org.apache.avalon.framework.logger.AbstractLogEnabled;
0037: import org.apache.avalon.framework.service.ServiceException;
0038: import org.apache.avalon.framework.service.ServiceManager;
0039: import org.apache.avalon.framework.service.ServiceSelector;
0040: import org.apache.avalon.framework.service.Serviceable;
0041: import org.apache.commons.io.IOUtils;
0042: import org.apache.commons.io.output.ByteArrayOutputStream;
0043: import org.apache.excalibur.source.Source;
0044: import org.apache.excalibur.source.SourceResolver;
0045: import org.apache.lenya.cms.metadata.MetaData;
0046: import org.apache.lenya.cms.metadata.MetaDataException;
0047: import org.apache.lenya.cms.publication.util.DocumentSet;
0048: import org.apache.lenya.cms.publication.util.DocumentVisitor;
0049: import org.apache.lenya.cms.repository.Node;
0050: import org.apache.lenya.cms.repository.RepositoryException;
0051: import org.apache.lenya.cms.repository.UUIDGenerator;
0052: import org.apache.lenya.cms.site.Link;
0053: import org.apache.lenya.cms.site.NodeIterator;
0054: import org.apache.lenya.cms.site.NodeSet;
0055: import org.apache.lenya.cms.site.SiteException;
0056: import org.apache.lenya.cms.site.SiteManager;
0057: import org.apache.lenya.cms.site.SiteNode;
0058: import org.apache.lenya.cms.site.SiteStructure;
0059: import org.apache.lenya.cms.site.SiteUtil;
0060: import org.apache.lenya.util.Assert;
0061:
0062: /**
0063: * DocumentManager implementation.
0064: *
0065: * @version $Id: DocumentManagerImpl.java 602869 2007-12-10 11:41:12Z andreas $
0066: */
0067: public class DocumentManagerImpl extends AbstractLogEnabled implements
0068: DocumentManager, Serviceable, Contextualizable {
0069:
0070: /**
0071: * @see org.apache.lenya.cms.publication.DocumentManager#add(org.apache.lenya.cms.publication.Document,
0072: * java.lang.String, java.lang.String, java.lang.String,
0073: * java.lang.String, java.lang.String, boolean)
0074: */
0075: public Document add(Document sourceDocument, String area,
0076: String path, String language, String extension,
0077: String navigationTitle, boolean visibleInNav)
0078: throws DocumentBuildException, PublicationException {
0079:
0080: Document document = add(sourceDocument.getFactory(),
0081: sourceDocument.getResourceType(), sourceDocument
0082: .getInputStream(), sourceDocument
0083: .getPublication(), area, path, language,
0084: extension, navigationTitle, visibleInNav,
0085: sourceDocument.getMimeType());
0086:
0087: copyMetaData(sourceDocument, document);
0088: return document;
0089: }
0090:
0091: /**
0092: * Copies meta data from one document to another. If the destination
0093: * document is a different area version, the meta data are duplicated (i.e.,
0094: * onCopy = delete is neglected).
0095: * @param source
0096: * @param destination
0097: * @throws PublicationException
0098: */
0099: protected void copyMetaData(Document source, Document destination)
0100: throws PublicationException {
0101:
0102: boolean duplicate = source.getUUID().equals(
0103: destination.getUUID())
0104: && source.getLanguage().equals(
0105: destination.getLanguage())
0106: && !source.getArea().equals(destination.getArea());
0107:
0108: try {
0109: String[] uris = source.getMetaDataNamespaceUris();
0110: for (int i = 0; i < uris.length; i++) {
0111: if (duplicate) {
0112: destination.getMetaData(uris[i]).forcedReplaceBy(
0113: source.getMetaData(uris[i]));
0114: } else {
0115: destination.getMetaData(uris[i]).replaceBy(
0116: source.getMetaData(uris[i]));
0117: }
0118: }
0119: } catch (MetaDataException e) {
0120: throw new PublicationException(e);
0121: }
0122: }
0123:
0124: /**
0125: * @see org.apache.lenya.cms.publication.DocumentManager#add(org.apache.lenya.cms.publication.DocumentFactory,
0126: * org.apache.lenya.cms.publication.ResourceType, java.lang.String,
0127: * org.apache.lenya.cms.publication.Publication, java.lang.String,
0128: * java.lang.String, java.lang.String, java.lang.String,
0129: * java.lang.String, boolean)
0130: */
0131: public Document add(DocumentFactory factory,
0132: ResourceType documentType, String initialContentsURI,
0133: Publication pub, String area, String path, String language,
0134: String extension, String navigationTitle,
0135: boolean visibleInNav) throws DocumentBuildException,
0136: DocumentException, PublicationException {
0137:
0138: Area areaObj = pub.getArea(area);
0139: SiteStructure site = areaObj.getSite();
0140: if (site.contains(path) && site.getNode(path).hasLink(language)) {
0141: throw new DocumentException("The link [" + path + ":"
0142: + language + "] is already contained in site ["
0143: + site + "]");
0144: }
0145:
0146: Document document = add(factory, documentType,
0147: initialContentsURI, pub, area, language, extension);
0148:
0149: addToSiteManager(path, document, navigationTitle, visibleInNav);
0150: return document;
0151: }
0152:
0153: protected Document add(DocumentFactory factory,
0154: ResourceType documentType,
0155: InputStream initialContentsStream, Publication pub,
0156: String area, String path, String language,
0157: String extension, String navigationTitle,
0158: boolean visibleInNav, String mimeType)
0159: throws DocumentBuildException, DocumentException,
0160: PublicationException {
0161:
0162: Area areaObj = pub.getArea(area);
0163: SiteStructure site = areaObj.getSite();
0164: if (site.contains(path) && site.getNode(path).hasLink(language)) {
0165: throw new DocumentException("The link [" + path + ":"
0166: + language + "] is already contained in site ["
0167: + site + "]");
0168: }
0169:
0170: Document document = add(factory, documentType,
0171: initialContentsStream, pub, area, language, extension,
0172: mimeType);
0173:
0174: addToSiteManager(path, document, navigationTitle, visibleInNav);
0175: return document;
0176: }
0177:
0178: public Document add(DocumentFactory factory,
0179: ResourceType documentType, String initialContentsURI,
0180: Publication pub, String area, String language,
0181: String extension) throws DocumentBuildException,
0182: DocumentException, PublicationException {
0183:
0184: String uuid = generateUUID();
0185: SourceResolver resolver = null;
0186: Source source = null;
0187: try {
0188: resolver = (SourceResolver) this .manager
0189: .lookup(SourceResolver.ROLE);
0190: source = resolver.resolveURI(initialContentsURI);
0191: return add(factory, documentType, uuid, source
0192: .getInputStream(), pub, area, language, extension,
0193: getMimeType(source));
0194: } catch (Exception e) {
0195: throw new PublicationException(e);
0196: } finally {
0197: if (resolver != null) {
0198: if (source != null) {
0199: resolver.release(source);
0200: }
0201: this .manager.release(resolver);
0202: }
0203: }
0204: }
0205:
0206: protected String getMimeType(Source source) {
0207: String mimeType = source.getMimeType();
0208: if (mimeType == null) {
0209: mimeType = "";
0210: }
0211: return mimeType;
0212: }
0213:
0214: protected Document add(DocumentFactory factory,
0215: ResourceType documentType,
0216: InputStream initialContentsStream, Publication pub,
0217: String area, String language, String extension,
0218: String mimeType) throws DocumentBuildException,
0219: DocumentException, PublicationException {
0220:
0221: String uuid = generateUUID();
0222: return add(factory, documentType, uuid, initialContentsStream,
0223: pub, area, language, extension, mimeType);
0224: }
0225:
0226: protected Document add(DocumentFactory factory,
0227: ResourceType documentType, String uuid, InputStream stream,
0228: Publication pub, String area, String language,
0229: String extension, String mimeType)
0230: throws DocumentBuildException {
0231: try {
0232:
0233: if (exists(factory, pub, area, uuid, language)) {
0234: throw new DocumentBuildException("The document ["
0235: + pub.getId() + ":" + area + ":" + uuid + ":"
0236: + language + "] already exists!");
0237: }
0238:
0239: Document document = factory.get(pub, area, uuid, language);
0240: Node node = document.getRepositoryNode();
0241: node.lock();
0242:
0243: document.setResourceType(documentType);
0244: document.setSourceExtension(extension);
0245: document.setMimeType(mimeType);
0246:
0247: // Write Lenya-internal meta-data
0248: MetaData lenyaMetaData = document
0249: .getMetaData(DocumentImpl.METADATA_NAMESPACE);
0250: lenyaMetaData.setValue(DocumentImpl.METADATA_CONTENT_TYPE,
0251: "xml");
0252:
0253: if (getLogger().isDebugEnabled()) {
0254: getLogger().debug("Create");
0255: getLogger().debug(
0256: " document: [" + document + "]");
0257: }
0258:
0259: create(stream, document);
0260: return document;
0261: } catch (Exception e) {
0262: throw new DocumentBuildException(
0263: "call to creator for new document failed", e);
0264: }
0265: }
0266:
0267: protected String generateUUID() throws DocumentBuildException {
0268: String uuid;
0269: UUIDGenerator generator = null;
0270: try {
0271:
0272: generator = (UUIDGenerator) this .manager
0273: .lookup(UUIDGenerator.ROLE);
0274: uuid = generator.nextUUID();
0275:
0276: } catch (Exception e) {
0277: throw new DocumentBuildException(
0278: "call to creator for new document failed", e);
0279: } finally {
0280: if (generator != null) {
0281: this .manager.release(generator);
0282: }
0283: }
0284: return uuid;
0285: }
0286:
0287: protected void create(InputStream stream, Document document)
0288: throws Exception {
0289:
0290: // Read initial contents as DOM
0291: if (getLogger().isDebugEnabled())
0292: getLogger().debug(
0293: "DefaultCreator::create(), ready to read initial contents from URI ["
0294: + stream + "]");
0295:
0296: SourceResolver resolver = null;
0297: try {
0298: resolver = (SourceResolver) this .manager
0299: .lookup(SourceResolver.ROLE);
0300: copy(resolver, stream, document);
0301: } finally {
0302: if (resolver != null) {
0303: this .manager.release(resolver);
0304: }
0305: }
0306: }
0307:
0308: protected void copy(SourceResolver resolver,
0309: InputStream sourceInputStream, Document destination)
0310: throws IOException {
0311:
0312: boolean useBuffer = true;
0313:
0314: OutputStream destOutputStream = null;
0315: try {
0316: destOutputStream = destination.getOutputStream();
0317:
0318: if (useBuffer) {
0319: final ByteArrayOutputStream sourceBos = new ByteArrayOutputStream();
0320: IOUtils.copy(sourceInputStream, sourceBos);
0321: IOUtils
0322: .write(sourceBos.toByteArray(),
0323: destOutputStream);
0324: } else {
0325: IOUtils.copy(sourceInputStream, destOutputStream);
0326: }
0327: } finally {
0328: if (destOutputStream != null) {
0329: destOutputStream.flush();
0330: destOutputStream.close();
0331: }
0332: if (sourceInputStream != null) {
0333: sourceInputStream.close();
0334: }
0335: }
0336: }
0337:
0338: protected void addToSiteManager(String path, Document document,
0339: String navigationTitle, boolean visibleInNav)
0340: throws PublicationException {
0341: addToSiteManager(path, document, navigationTitle, visibleInNav,
0342: null);
0343: }
0344:
0345: protected void addToSiteManager(String path, Document document,
0346: String navigationTitle, boolean visibleInNav,
0347: String followingSiblingPath) throws PublicationException {
0348: SiteStructure site = document.area().getSite();
0349: if (!site.contains(path) && followingSiblingPath != null) {
0350: site.add(path, followingSiblingPath);
0351: }
0352: site.add(path, document);
0353: document.getLink().setLabel(navigationTitle);
0354: document.getLink().getNode().setVisible(visibleInNav);
0355: }
0356:
0357: /**
0358: * Template method to copy a document. Override
0359: * {@link #copyDocumentSource(Document, Document)} to implement access to a
0360: * custom repository.
0361: * @see org.apache.lenya.cms.publication.DocumentManager#copy(org.apache.lenya.cms.publication.Document,
0362: * org.apache.lenya.cms.publication.DocumentLocator)
0363: */
0364: public void copy(Document sourceDoc, DocumentLocator destination)
0365: throws PublicationException {
0366:
0367: if (!destination.getPublicationId().equals(
0368: sourceDoc.getPublication().getId())) {
0369: throw new PublicationException(
0370: "Can't copy to a different publication!");
0371: }
0372:
0373: SiteStructure destSite = sourceDoc.getPublication().getArea(
0374: destination.getArea()).getSite();
0375: String destPath = destination.getPath();
0376: if (destSite.contains(destination.getPath(), destination
0377: .getLanguage())) {
0378: Document destDoc = destSite.getNode(destPath).getLink(
0379: destination.getLanguage()).getDocument();
0380: copyDocumentSource(sourceDoc, destDoc);
0381: copyInSiteStructure(sourceDoc, destDoc, destPath);
0382: } else {
0383: add(sourceDoc, destination.getArea(), destPath, destination
0384: .getLanguage(), sourceDoc.getExtension(), sourceDoc
0385: .getLink().getLabel(), sourceDoc.getLink()
0386: .getNode().isVisible());
0387: }
0388:
0389: }
0390:
0391: protected void copyInSiteStructure(Document sourceDoc,
0392: Document destDoc, String destPath)
0393: throws PublicationException, DocumentException,
0394: SiteException {
0395:
0396: String destArea = destDoc.getArea();
0397:
0398: SiteStructure destSite = sourceDoc.getPublication().getArea(
0399: destArea).getSite();
0400:
0401: if (sourceDoc.hasLink()) {
0402: if (destDoc.hasLink()) {
0403: Link srcLink = sourceDoc.getLink();
0404: Link destLink = destDoc.getLink();
0405: destLink.setLabel(srcLink.getLabel());
0406: destLink.getNode().setVisible(
0407: srcLink.getNode().isVisible());
0408: } else {
0409: String label = sourceDoc.getLink().getLabel();
0410: boolean visible = sourceDoc.getLink().getNode()
0411: .isVisible();
0412: if (destSite.contains(sourceDoc.getLink().getNode()
0413: .getPath())) {
0414: addToSiteManager(destPath, destDoc, label, visible);
0415: } else {
0416:
0417: String followingSiblingPath = null;
0418:
0419: if (sourceDoc.getPath().equals(destPath)) {
0420: SiteStructure sourceSite = sourceDoc.area()
0421: .getSite();
0422:
0423: SiteNode[] sourceSiblings;
0424: SiteNode sourceNode = sourceDoc.getLink()
0425: .getNode();
0426: if (sourceNode.isTopLevel()) {
0427: sourceSiblings = sourceSite
0428: .getTopLevelNodes();
0429: } else if (sourceNode.getParent() != null) {
0430: sourceSiblings = sourceNode.getParent()
0431: .getChildren();
0432: } else {
0433: sourceSiblings = new SiteNode[1];
0434: sourceSiblings[0] = sourceNode;
0435: }
0436:
0437: final int sourcePos = Arrays.asList(
0438: sourceSiblings).indexOf(sourceNode);
0439:
0440: int pos = sourcePos;
0441: while (followingSiblingPath == null
0442: && pos < sourceSiblings.length) {
0443: String siblingPath = sourceSiblings[pos]
0444: .getPath();
0445: if (destSite.contains(siblingPath)) {
0446: followingSiblingPath = siblingPath;
0447: }
0448: pos++;
0449: }
0450: }
0451:
0452: if (followingSiblingPath == null) {
0453: addToSiteManager(destPath, destDoc, label,
0454: visible);
0455: } else {
0456: addToSiteManager(destPath, destDoc, label,
0457: visible, followingSiblingPath);
0458: }
0459: }
0460: }
0461: }
0462: }
0463:
0464: /**
0465: * @see org.apache.lenya.cms.publication.DocumentManager#delete(org.apache.lenya.cms.publication.Document)
0466: */
0467: public void delete(Document document) throws PublicationException {
0468: if (!document.exists()) {
0469: throw new PublicationException("Document [" + document
0470: + "] does not exist!");
0471: }
0472:
0473: if (document.hasLink()) {
0474: document.getLink().delete();
0475: }
0476:
0477: document.delete();
0478: }
0479:
0480: /**
0481: * @see org.apache.lenya.cms.publication.DocumentManager#move(org.apache.lenya.cms.publication.Document,
0482: * org.apache.lenya.cms.publication.DocumentLocator)
0483: */
0484: public void move(Document sourceDocument,
0485: DocumentLocator destination) throws PublicationException {
0486:
0487: if (!destination.getArea().equals(sourceDocument.getArea())) {
0488: throw new PublicationException(
0489: "Can't move to a different area!");
0490: }
0491:
0492: SiteStructure site = sourceDocument.area().getSite();
0493: if (site.contains(destination.getPath())) {
0494: throw new PublicationException("The path [" + destination
0495: + "] is already contained in this publication!");
0496: }
0497:
0498: String label = sourceDocument.getLink().getLabel();
0499: boolean visible = sourceDocument.getLink().getNode()
0500: .isVisible();
0501: sourceDocument.getLink().delete();
0502:
0503: site.add(destination.getPath(), sourceDocument);
0504: sourceDocument.getLink().setLabel(label);
0505: sourceDocument.getLink().getNode().setVisible(visible);
0506:
0507: }
0508:
0509: /**
0510: * @see org.apache.lenya.cms.publication.DocumentManager#copyToArea(org.apache.lenya.cms.publication.Document,
0511: * java.lang.String)
0512: */
0513: public void copyToArea(Document sourceDoc, String destinationArea)
0514: throws PublicationException {
0515: String language = sourceDoc.getLanguage();
0516: copyToVersion(sourceDoc, destinationArea, language);
0517: }
0518:
0519: protected void copyToVersion(Document sourceDoc,
0520: String destinationArea, String language)
0521: throws DocumentException, DocumentBuildException,
0522: PublicationException, SiteException {
0523:
0524: Document destDoc;
0525: if (sourceDoc.existsAreaVersion(destinationArea)) {
0526: destDoc = sourceDoc.getAreaVersion(destinationArea);
0527: copyDocumentSource(sourceDoc, destDoc);
0528: } else {
0529: destDoc = addVersion(sourceDoc, destinationArea, language);
0530: }
0531:
0532: if (sourceDoc.hasLink()) {
0533: copyInSiteStructure(sourceDoc, destDoc, sourceDoc.getPath());
0534: }
0535: }
0536:
0537: /**
0538: * @see org.apache.lenya.cms.publication.DocumentManager#copyToArea(org.apache.lenya.cms.publication.util.DocumentSet,
0539: * java.lang.String)
0540: */
0541: public void copyToArea(DocumentSet documentSet,
0542: String destinationArea) throws PublicationException {
0543: Document[] documents = documentSet.getDocuments();
0544: for (int i = 0; i < documents.length; i++) {
0545: copyToArea(documents[i], destinationArea);
0546: }
0547: }
0548:
0549: protected ServiceManager manager;
0550:
0551: /**
0552: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
0553: */
0554: public void service(ServiceManager _manager)
0555: throws ServiceException {
0556: this .manager = _manager;
0557: }
0558:
0559: private Context context;
0560:
0561: /**
0562: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
0563: */
0564: public void contextualize(Context _context) throws ContextException {
0565: this .context = _context;
0566: }
0567:
0568: /**
0569: * @return The Avalon context.
0570: */
0571: protected Context getContext() {
0572: return this .context;
0573: }
0574:
0575: public void moveAll(Area sourceArea, String sourcePath,
0576: Area targetArea, String targetPath)
0577: throws PublicationException {
0578: SiteStructure site = sourceArea.getSite();
0579:
0580: SiteNode root = site.getNode(sourcePath);
0581: List subsite = preOrder(root);
0582:
0583: for (Iterator n = subsite.iterator(); n.hasNext();) {
0584: SiteNode node = (SiteNode) n.next();
0585: String subPath = node.getPath().substring(
0586: sourcePath.length());
0587: targetArea.getSite().add(targetPath + subPath);
0588: }
0589: Collections.reverse(subsite);
0590: for (Iterator n = subsite.iterator(); n.hasNext();) {
0591: SiteNode node = (SiteNode) n.next();
0592: String subPath = node.getPath().substring(
0593: sourcePath.length());
0594: moveAllLanguageVersions(sourceArea, sourcePath + subPath,
0595: targetArea, targetPath + subPath);
0596: }
0597: }
0598:
0599: protected List preOrder(SiteNode node) {
0600: List list = new ArrayList();
0601: list.add(node);
0602: SiteNode[] children = node.getChildren();
0603: for (int i = 0; i < children.length; i++) {
0604: list.addAll(preOrder(children[i]));
0605: }
0606: return list;
0607: }
0608:
0609: public void moveAllLanguageVersions(Area sourceArea,
0610: String sourcePath, Area targetArea, String targetPath)
0611: throws PublicationException {
0612:
0613: SiteNode sourceNode = sourceArea.getSite().getNode(sourcePath);
0614: String[] languages = sourceNode.getLanguages();
0615: for (int i = 0; i < languages.length; i++) {
0616: Link sourceLink = sourceNode.getLink(languages[i]);
0617: String label = sourceLink.getLabel();
0618: Document sourceDoc = sourceLink.getDocument();
0619: sourceLink.delete();
0620:
0621: Document targetDoc;
0622: if (sourceArea.getName().equals(targetArea.getName())) {
0623: targetDoc = sourceDoc;
0624: } else {
0625: targetDoc = addVersion(sourceDoc, targetArea.getName(),
0626: sourceDoc.getLanguage());
0627: copyRevisions(sourceDoc, targetDoc);
0628: sourceDoc.delete();
0629: }
0630:
0631: Link link = targetArea.getSite().add(targetPath, targetDoc);
0632: link.setLabel(label);
0633: Assert.isTrue("label set", targetDoc.getLink().getLabel()
0634: .equals(label));
0635: }
0636: SiteNode targetNode = targetArea.getSite().getNode(targetPath);
0637: targetNode.setVisible(sourceNode.isVisible());
0638: }
0639:
0640: protected void copyRevisions(Document sourceDoc, Document targetDoc)
0641: throws PublicationException {
0642: try {
0643: Node targetNode = targetDoc.getRepositoryNode();
0644: targetNode.copyRevisionsFrom(sourceDoc.getRepositoryNode());
0645: } catch (Exception e) {
0646: throw new PublicationException(e);
0647: }
0648: }
0649:
0650: public void copyAll(Area sourceArea, String sourcePath,
0651: Area targetArea, String targetPath)
0652: throws PublicationException {
0653:
0654: SiteStructure site = sourceArea.getSite();
0655: SiteNode root = site.getNode(sourcePath);
0656:
0657: List preOrder = preOrder(root);
0658: for (Iterator i = preOrder.iterator(); i.hasNext();) {
0659: SiteNode node = (SiteNode) i.next();
0660: String nodeSourcePath = node.getPath();
0661: String nodeTargetPath = targetPath
0662: + nodeSourcePath.substring(sourcePath.length());
0663: copyAllLanguageVersions(sourceArea, nodeSourcePath,
0664: targetArea, nodeTargetPath);
0665: }
0666: }
0667:
0668: public void copyAllLanguageVersions(Area sourceArea,
0669: String sourcePath, Area targetArea, String targetPath)
0670: throws PublicationException {
0671: Publication pub = sourceArea.getPublication();
0672:
0673: SiteNode sourceNode = sourceArea.getSite().getNode(sourcePath);
0674: String[] languages = sourceNode.getLanguages();
0675:
0676: Document targetDoc = null;
0677:
0678: for (int i = 0; i < languages.length; i++) {
0679: Document sourceVersion = sourceNode.getLink(languages[i])
0680: .getDocument();
0681: DocumentLocator targetLocator = DocumentLocator.getLocator(
0682: pub.getId(), targetArea.getName(), targetPath,
0683: languages[i]);
0684: if (targetDoc == null) {
0685: copy(sourceVersion, targetLocator
0686: .getLanguageVersion(languages[i]));
0687: targetDoc = targetArea.getSite().getNode(targetPath)
0688: .getLink(languages[i]).getDocument();
0689: } else {
0690: targetDoc = addVersion(targetDoc, targetLocator
0691: .getArea(), languages[i]);
0692: addToSiteManager(targetLocator.getPath(), targetDoc,
0693: sourceVersion.getLink().getLabel(),
0694: sourceVersion.getLink().getNode().isVisible());
0695: copyDocumentSource(sourceVersion, targetDoc);
0696: }
0697: }
0698: }
0699:
0700: /**
0701: * Copies a document source.
0702: * @param sourceDocument The source document.
0703: * @param destinationDocument The destination document.
0704: * @throws PublicationException when something went wrong.
0705: */
0706: public void copyDocumentSource(Document sourceDocument,
0707: Document destinationDocument) throws PublicationException {
0708: copyContent(sourceDocument, destinationDocument);
0709: copyMetaData(sourceDocument, destinationDocument);
0710: }
0711:
0712: protected void copyContent(Document sourceDocument,
0713: Document destinationDocument) throws PublicationException {
0714: boolean useBuffer = true;
0715:
0716: OutputStream destOutputStream = null;
0717: InputStream sourceInputStream = null;
0718: try {
0719: try {
0720: sourceInputStream = sourceDocument.getInputStream();
0721: destOutputStream = destinationDocument
0722: .getOutputStream();
0723:
0724: if (useBuffer) {
0725: final ByteArrayOutputStream sourceBos = new ByteArrayOutputStream();
0726: IOUtils.copy(sourceInputStream, sourceBos);
0727: IOUtils.write(sourceBos.toByteArray(),
0728: destOutputStream);
0729: } else {
0730: IOUtils.copy(sourceInputStream, destOutputStream);
0731: }
0732: } finally {
0733: if (destOutputStream != null) {
0734: destOutputStream.flush();
0735: destOutputStream.close();
0736: }
0737: if (sourceInputStream != null) {
0738: sourceInputStream.close();
0739: }
0740: }
0741: } catch (Exception e) {
0742: throw new PublicationException(e);
0743: }
0744: }
0745:
0746: /**
0747: * Abstract base class for document visitors which operate on a source and
0748: * target document.
0749: */
0750: public abstract class SourceTargetVisitor implements
0751: DocumentVisitor {
0752:
0753: private DocumentLocator rootSource;
0754: private DocumentLocator rootTarget;
0755: private DocumentManager manager;
0756:
0757: /**
0758: * Ctor.
0759: * @param manager The document manager.
0760: * @param source The root source.
0761: * @param target The root target.
0762: */
0763: public SourceTargetVisitor(DocumentManager manager,
0764: Document source, DocumentLocator target) {
0765: this .manager = manager;
0766: this .rootSource = source.getLocator();
0767: this .rootTarget = target;
0768: }
0769:
0770: /**
0771: * @return the root source
0772: */
0773: protected DocumentLocator getRootSource() {
0774: return rootSource;
0775: }
0776:
0777: /**
0778: * @return the root target
0779: */
0780: protected DocumentLocator getRootTarget() {
0781: return rootTarget;
0782: }
0783:
0784: /**
0785: * @return the document manager
0786: */
0787: protected DocumentManager getDocumentManager() {
0788: return this .manager;
0789: }
0790:
0791: /**
0792: * Returns the target corresponding to a source relatively to the root
0793: * target document.
0794: * @param source The source.
0795: * @return A document.
0796: * @throws DocumentBuildException if the target could not be built.
0797: */
0798: protected DocumentLocator getTarget(Document source)
0799: throws DocumentBuildException {
0800: DocumentLocator sourceLocator = source.getLocator();
0801: String rootSourcePath = getRootSource().getPath();
0802: if (sourceLocator.getPath().equals(rootSourcePath)) {
0803: return rootTarget;
0804: } else {
0805: String relativePath = sourceLocator.getPath()
0806: .substring(rootSourcePath.length());
0807: return rootTarget.getDescendant(relativePath);
0808: }
0809: }
0810: }
0811:
0812: /**
0813: * @see org.apache.lenya.cms.publication.DocumentManager#deleteAll(org.apache.lenya.cms.publication.Document)
0814: */
0815: public void deleteAll(Document document)
0816: throws PublicationException {
0817:
0818: SiteManager siteManager = null;
0819: ServiceSelector selector = null;
0820: try {
0821: selector = (ServiceSelector) this .manager
0822: .lookup(SiteManager.ROLE + "Selector");
0823: siteManager = (SiteManager) selector.select(document
0824: .getPublication().getSiteManagerHint());
0825:
0826: NodeSet subsite = SiteUtil.getSubSite(this .manager,
0827: document.getLink().getNode());
0828: for (NodeIterator i = subsite.descending(); i.hasNext();) {
0829: SiteNode node = i.next();
0830: String[] languages = node.getLanguages();
0831: for (int l = 0; l < languages.length; l++) {
0832: Document doc = node.getLink(languages[l])
0833: .getDocument();
0834: delete(doc);
0835: }
0836: }
0837: } catch (ServiceException e) {
0838: throw new PublicationException(e);
0839: } finally {
0840: if (selector != null) {
0841: if (siteManager != null) {
0842: selector.release(siteManager);
0843: }
0844: this .manager.release(selector);
0845: }
0846: }
0847: }
0848:
0849: /**
0850: * @see org.apache.lenya.cms.publication.DocumentManager#deleteAllLanguageVersions(org.apache.lenya.cms.publication.Document)
0851: */
0852: public void deleteAllLanguageVersions(Document document)
0853: throws PublicationException {
0854: DocumentFactory identityMap = document.getFactory();
0855: String[] languages = document.getLanguages();
0856: for (int i = 0; i < languages.length; i++) {
0857: DocumentLocator version = document.getLocator()
0858: .getLanguageVersion(languages[i]);
0859: delete(identityMap.get(version));
0860: }
0861: }
0862:
0863: /**
0864: * Visitor to delete documents.
0865: */
0866: public class DeleteVisitor implements DocumentVisitor {
0867:
0868: private DocumentManager manager;
0869:
0870: /**
0871: * Ctor.
0872: * @param manager The document manager.
0873: */
0874: public DeleteVisitor(DocumentManager manager) {
0875: this .manager = manager;
0876: }
0877:
0878: protected DocumentManager getDocumentManager() {
0879: return this .manager;
0880: }
0881:
0882: /**
0883: * @see org.apache.lenya.cms.publication.util.DocumentVisitor#visitDocument(org.apache.lenya.cms.publication.Document)
0884: */
0885: public void visitDocument(Document document)
0886: throws PublicationException {
0887: getDocumentManager().deleteAllLanguageVersions(document);
0888: }
0889:
0890: }
0891:
0892: /**
0893: * @see org.apache.lenya.cms.publication.DocumentManager#delete(org.apache.lenya.cms.publication.util.DocumentSet)
0894: */
0895: public void delete(DocumentSet documents)
0896: throws PublicationException {
0897:
0898: if (documents.isEmpty()) {
0899: return;
0900: }
0901:
0902: SiteManager siteManager = null;
0903: ServiceSelector selector = null;
0904: try {
0905: selector = (ServiceSelector) this .manager
0906: .lookup(SiteManager.ROLE + "Selector");
0907: Publication pub = documents.getDocuments()[0]
0908: .getPublication();
0909: siteManager = (SiteManager) selector.select(pub
0910: .getSiteManagerHint());
0911:
0912: DocumentSet set = new DocumentSet(documents.getDocuments());
0913: sortAscending(set);
0914: set.reverse();
0915:
0916: DocumentVisitor visitor = new DeleteVisitor(this );
0917: set.visit(visitor);
0918: } catch (ServiceException e) {
0919: throw new PublicationException(e);
0920: } finally {
0921: if (selector != null) {
0922: if (siteManager != null) {
0923: selector.release(siteManager);
0924: }
0925: this .manager.release(selector);
0926: }
0927: }
0928:
0929: }
0930:
0931: /**
0932: * @see org.apache.lenya.cms.publication.DocumentManager#move(org.apache.lenya.cms.publication.util.DocumentSet,
0933: * org.apache.lenya.cms.publication.util.DocumentSet)
0934: */
0935: public void move(DocumentSet sources, DocumentSet destinations)
0936: throws PublicationException {
0937: copy(sources, destinations);
0938: delete(sources);
0939: /*
0940: * Document[] sourceDocs = sources.getDocuments(); Document[] targetDocs =
0941: * destinations.getDocuments();
0942: *
0943: * if (sourceDocs.length != targetDocs.length) { throw new
0944: * PublicationException( "The number of source and destination documents
0945: * must be equal!"); }
0946: *
0947: * Map source2target = new HashMap(); for (int i = 0; i <
0948: * sourceDocs.length; i++) { source2target.put(sourceDocs[i],
0949: * targetDocs[i]); }
0950: *
0951: * DocumentSet sortedSources = new DocumentSet(sourceDocs);
0952: * SiteUtil.sortAscending(this.manager, sortedSources); Document[]
0953: * sortedSourceDocs = sortedSources.getDocuments();
0954: *
0955: * for (int i = 0; i < sortedSourceDocs.length; i++) {
0956: * move(sortedSourceDocs[i], (Document)
0957: * source2target.get(sortedSourceDocs[i])); }
0958: */
0959: }
0960:
0961: /**
0962: * @see org.apache.lenya.cms.publication.DocumentManager#copy(org.apache.lenya.cms.publication.util.DocumentSet,
0963: * org.apache.lenya.cms.publication.util.DocumentSet)
0964: */
0965: public void copy(DocumentSet sources, DocumentSet destinations)
0966: throws PublicationException {
0967: Document[] sourceDocs = sources.getDocuments();
0968: Document[] targetDocs = destinations.getDocuments();
0969:
0970: if (sourceDocs.length != targetDocs.length) {
0971: throw new PublicationException(
0972: "The number of source and destination documents must be equal!");
0973: }
0974:
0975: Map source2target = new HashMap();
0976: for (int i = 0; i < sourceDocs.length; i++) {
0977: source2target.put(sourceDocs[i], targetDocs[i]);
0978: }
0979:
0980: DocumentSet sortedSources = new DocumentSet(sourceDocs);
0981: sortAscending(sortedSources);
0982: Document[] sortedSourceDocs = sortedSources.getDocuments();
0983:
0984: for (int i = 0; i < sortedSourceDocs.length; i++) {
0985: copy(sortedSourceDocs[i], ((Document) source2target
0986: .get(sortedSourceDocs[i])).getLocator());
0987: }
0988: }
0989:
0990: protected void sortAscending(DocumentSet set)
0991: throws PublicationException {
0992:
0993: if (!set.isEmpty()) {
0994:
0995: Document[] docs = set.getDocuments();
0996: int n = docs.length;
0997:
0998: Publication pub = docs[0].getPublication();
0999: SiteManager siteManager = null;
1000: ServiceSelector selector = null;
1001: try {
1002: selector = (ServiceSelector) this .manager
1003: .lookup(SiteManager.ROLE + "Selector");
1004: siteManager = (SiteManager) selector.select(pub
1005: .getSiteManagerHint());
1006:
1007: Set nodes = new HashSet();
1008: for (int i = 0; i < docs.length; i++) {
1009: nodes.add(docs[i].getLink().getNode());
1010: }
1011:
1012: SiteNode[] ascending = siteManager
1013: .sortAscending((SiteNode[]) nodes
1014: .toArray(new SiteNode[nodes.size()]));
1015:
1016: set.clear();
1017: for (int i = 0; i < ascending.length; i++) {
1018: for (int d = 0; d < docs.length; d++) {
1019: if (docs[d].getPath().equals(
1020: ascending[i].getPath())) {
1021: set.add(docs[d]);
1022: }
1023: }
1024: }
1025:
1026: if (set.getDocuments().length != n) {
1027: throw new IllegalStateException(
1028: "Number of documents has changed!");
1029: }
1030:
1031: } catch (final ServiceException e) {
1032: throw new PublicationException(e);
1033: } finally {
1034: if (selector != null) {
1035: if (siteManager != null) {
1036: selector.release(siteManager);
1037: }
1038: this .manager.release(selector);
1039: }
1040: }
1041: }
1042: }
1043:
1044: public Document addVersion(Document sourceDocument, String area,
1045: String language, boolean addToSiteStructure)
1046: throws DocumentBuildException, PublicationException {
1047: Document document = addVersion(sourceDocument, area, language);
1048:
1049: if (addToSiteStructure && sourceDocument.hasLink()) {
1050: String path = sourceDocument.getPath();
1051: boolean visible = sourceDocument.getLink().getNode()
1052: .isVisible();
1053: addToSiteManager(path, document, sourceDocument.getLink()
1054: .getLabel(), visible);
1055: }
1056:
1057: return document;
1058: }
1059:
1060: public Document addVersion(Document sourceDocument, String area,
1061: String language) throws DocumentBuildException,
1062: DocumentException, PublicationException {
1063: Document document = add(sourceDocument.getFactory(),
1064: sourceDocument.getResourceType(), sourceDocument
1065: .getUUID(), sourceDocument.getInputStream(),
1066: sourceDocument.getPublication(), area, language,
1067: sourceDocument.getSourceExtension(), sourceDocument
1068: .getMimeType());
1069: copyMetaData(sourceDocument, document);
1070:
1071: return document;
1072: }
1073:
1074: public boolean exists(DocumentFactory factory, Publication pub,
1075: String area, String uuid, String language)
1076: throws PublicationException {
1077: String sourceUri = DocumentImpl.getSourceURI(pub, area, uuid,
1078: language);
1079: try {
1080: Node node = DocumentImpl.getRepositoryNode(this .manager,
1081: factory, sourceUri);
1082: return node.exists();
1083: } catch (RepositoryException e) {
1084: throw new PublicationException(e);
1085: }
1086: }
1087:
1088: }
|