001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.lenya.cms.site.tree;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import org.apache.avalon.framework.service.Serviceable;
025: import org.apache.lenya.cms.publication.Document;
026: import org.apache.lenya.cms.publication.DocumentException;
027: import org.apache.lenya.cms.publication.DocumentFactory;
028: import org.apache.lenya.cms.publication.DocumentLocator;
029: import org.apache.lenya.cms.publication.Publication;
030: import org.apache.lenya.cms.publication.PublicationException;
031: import org.apache.lenya.cms.repository.RepositoryItemFactory;
032: import org.apache.lenya.cms.site.AbstractSiteManager;
033: import org.apache.lenya.cms.site.Link;
034: import org.apache.lenya.cms.site.NodeSet;
035: import org.apache.lenya.cms.site.SiteException;
036: import org.apache.lenya.cms.site.SiteNode;
037: import org.apache.lenya.cms.site.SiteStructure;
038:
039: /**
040: * A tree-based site manager.
041: *
042: * @version $Id: TreeSiteManager.java 208766 2005-07-01 16:05:00Z andreas $
043: */
044: public class TreeSiteManager extends AbstractSiteManager implements
045: Serviceable {
046:
047: /**
048: * Ctor.
049: */
050: public TreeSiteManager() {
051: // do nothing
052: }
053:
054: /**
055: * Returns the sitetree for a specific area of this publication. Sitetrees are created on demand
056: * and are cached.
057: *
058: * @param map The document identity map.
059: * @param publication The publication.
060: * @param area The area.
061: * @return A site tree.
062: * @throws SiteException if an error occurs.
063: */
064: public DefaultSiteTree getTree(DocumentFactory map,
065: Publication publication, String area) throws SiteException {
066:
067: String key = getKey(publication, area);
068: DefaultSiteTree sitetree;
069: RepositoryItemFactory factory = new SiteTreeFactory(
070: this .manager, getLogger());
071: try {
072: sitetree = (DefaultSiteTree) map.getSession()
073: .getRepositoryItem(factory, key);
074: } catch (Exception e) {
075: throw new SiteException(e);
076: }
077:
078: return sitetree;
079: }
080:
081: protected DefaultSiteTree getTree(Document document)
082: throws SiteException {
083: return getTree(document.getFactory(),
084: document.getPublication(), document.getArea());
085: }
086:
087: /**
088: * Returns the ancestors of a resource, beginning with the parent.
089: *
090: * @param node The resource.
091: * @return A list of resources.
092: * @throws SiteException if an error occurs.
093: */
094: protected List getAncestors(SiteNode node) throws SiteException {
095: List ancestors = new ArrayList();
096: SiteNode parent = null;
097: try {
098: if (!node.isTopLevel()) {
099: parent = node.getParent();
100: ancestors.add(parent);
101: ancestors.addAll(getAncestors(parent));
102: }
103: } catch (Exception e) {
104: throw new SiteException(e);
105: }
106: return ancestors;
107: }
108:
109: /**
110: * @see org.apache.lenya.cms.site.SiteManager#requires(org.apache.lenya.cms.publication.DocumentFactory,
111: * org.apache.lenya.cms.site.SiteNode, org.apache.lenya.cms.site.SiteNode)
112: */
113: public boolean requires(DocumentFactory map,
114: SiteNode dependingResource, SiteNode requiredResource)
115: throws SiteException {
116: return getAncestors(dependingResource).contains(
117: requiredResource);
118: }
119:
120: public DocumentLocator[] getRequiredResources(DocumentFactory map,
121: final DocumentLocator loc) throws SiteException {
122:
123: List ancestors = new ArrayList();
124: DocumentLocator locator = loc;
125: while (locator.getParent() != null) {
126: DocumentLocator parent = locator.getParent();
127: ancestors.add(parent);
128: locator = parent;
129: }
130: return (DocumentLocator[]) ancestors
131: .toArray(new DocumentLocator[ancestors.size()]);
132: }
133:
134: /**
135: * @see org.apache.lenya.cms.site.SiteManager#getRequiringResources(org.apache.lenya.cms.publication.DocumentFactory,
136: * org.apache.lenya.cms.site.SiteNode)
137: */
138: public SiteNode[] getRequiringResources(DocumentFactory map,
139: SiteNode resource) throws SiteException {
140:
141: if (getLogger().isDebugEnabled()) {
142: getLogger().debug(
143: "Obtaining requiring resources of [" + resource
144: + "]");
145: }
146:
147: NodeSet nodes = new NodeSet(this .manager);
148: Publication pub = resource.getStructure().getPublication();
149: String area = resource.getStructure().getArea();
150: SiteTree tree = getTree(map, pub, area);
151:
152: SiteTreeNodeImpl node = (SiteTreeNodeImpl) tree
153: .getNode(resource.getPath());
154: if (node != null) {
155: List preOrder = node.preOrder();
156:
157: // remove original resource (does not require itself)
158: preOrder.remove(0);
159:
160: for (int i = 0; i < preOrder.size(); i++) {
161: SiteTreeNode descendant = (SiteTreeNode) preOrder
162: .get(i);
163: nodes.add(descendant);
164: }
165:
166: if (getLogger().isDebugEnabled()) {
167: getLogger().debug(
168: "Obtaining requiring resources completed.");
169: }
170: }
171:
172: return nodes.getNodes();
173: }
174:
175: /**
176: * @see org.apache.lenya.cms.site.SiteManager#contains(org.apache.lenya.cms.publication.Document)
177: */
178: public boolean contains(Document resource) throws SiteException {
179: SiteTree tree = getTree(resource);
180: return tree.containsByUuid(resource.getUUID(), resource
181: .getLanguage());
182: }
183:
184: /**
185: * @see org.apache.lenya.cms.site.SiteManager#containsInAnyLanguage(org.apache.lenya.cms.publication.Document)
186: */
187: public boolean containsInAnyLanguage(Document resource)
188: throws SiteException {
189: SiteTree tree = getTree(resource);
190: return tree.containsInAnyLanguage(resource.getUUID());
191: }
192:
193: /**
194: * @see org.apache.lenya.cms.site.SiteManager#copy(org.apache.lenya.cms.publication.Document,
195: * org.apache.lenya.cms.publication.Document)
196: */
197: public void copy(Document sourceDocument,
198: Document destinationDocument) throws SiteException {
199: DefaultSiteTree destinationTree = getTree(destinationDocument);
200:
201: try {
202: SiteTreeNode sourceNode = (SiteTreeNode) sourceDocument
203: .getLink().getNode();
204:
205: SiteTreeNode[] siblings = sourceNode.getNextSiblings();
206: SiteNode parent = sourceNode.getParent();
207: String parentId = "";
208: if (parent != null) {
209: parentId = parent.getPath();
210: }
211: SiteTreeNode sibling = null;
212: String siblingPath = null;
213:
214: // same UUID -> insert at the same position
215: if (sourceDocument.getUUID().equals(
216: destinationDocument.getUUID())) {
217: for (int i = 0; i < siblings.length; i++) {
218: String path = parentId + "/"
219: + siblings[i].getName();
220: sibling = (SiteTreeNode) destinationTree
221: .getNode(path);
222: if (sibling != null) {
223: siblingPath = path;
224: break;
225: }
226: }
227: }
228:
229: if (!sourceNode.hasLink(sourceDocument.getLanguage())) {
230: // the node that we're trying to publish
231: // doesn't have this language
232: throw new SiteException("The node "
233: + sourceDocument.getPath()
234: + " doesn't contain a label for language "
235: + sourceDocument.getLanguage());
236: }
237: Link link = sourceNode
238: .getLink(sourceDocument.getLanguage());
239: SiteTreeNode destinationNode = (SiteTreeNode) destinationTree
240: .getNode(destinationDocument.getPath());
241: if (destinationNode == null) {
242: if (siblingPath == null) {
243: destinationTree.addNode(destinationDocument
244: .getPath(), destinationDocument.getUUID(),
245: sourceNode.isVisible(), sourceNode
246: .getHref(), sourceNode.getSuffix(),
247: sourceNode.hasLink());
248: destinationTree.addLabel(destinationDocument
249: .getPath(), destinationDocument
250: .getLanguage(), link.getLabel());
251: } else {
252: destinationTree.addNode(destinationDocument
253: .getPath(), destinationDocument.getUUID(),
254: sourceNode.isVisible(), sourceNode
255: .getHref(), sourceNode.getSuffix(),
256: sourceNode.hasLink(), siblingPath);
257: destinationTree.addLabel(destinationDocument
258: .getPath(), destinationDocument
259: .getLanguage(), link.getLabel());
260: }
261:
262: } else {
263: // if the node already exists in the live
264: // tree simply insert the label in the
265: // live tree
266: destinationDocument.getLink().setLabel(link.getLabel());
267: }
268: } catch (DocumentException e) {
269: throw new SiteException(e);
270: }
271:
272: }
273:
274: /**
275: * @see org.apache.lenya.cms.site.SiteManager#setVisibleInNav(org.apache.lenya.cms.publication.Document,
276: * boolean)
277: */
278: public void setVisibleInNav(Document document, boolean visibleInNav)
279: throws SiteException {
280: SiteTree tree = getTree(document);
281: try {
282: tree.getNode(document.getPath()).setVisible(visibleInNav);
283: } catch (DocumentException e) {
284: throw new SiteException(e);
285: }
286: }
287:
288: /**
289: * Returns the label object of a document.
290: *
291: * @param document The document.
292: * @return A label.
293: * @throws SiteException if an error occurs.
294: */
295: protected Link getLabelObject(Document document)
296: throws SiteException {
297: Link label = null;
298: SiteTree siteTree = getTree(document);
299: if (siteTree != null) {
300: SiteTreeNode node = (SiteTreeNode) siteTree.getByUuid(
301: document.getUUID(), document.getLanguage())
302: .getNode();
303: if (node == null) {
304: throw new SiteException("Node for document ["
305: + document + "] does not exist!");
306: }
307: label = (Link) node.getLink(document.getLanguage());
308: }
309:
310: if (label == null) {
311: throw new SiteException("The label of document ["
312: + document + "] is null!");
313: }
314:
315: return label;
316: }
317:
318: /**
319: * @see org.apache.lenya.cms.site.SiteManager#getDocuments(org.apache.lenya.cms.publication.DocumentFactory,
320: * org.apache.lenya.cms.publication.Publication, java.lang.String)
321: */
322: public Document[] getDocuments(DocumentFactory map,
323: Publication publication, String area) throws SiteException {
324: try {
325: SiteTreeNodeImpl root = (SiteTreeNodeImpl) getTree(map,
326: publication, area).getNode("/");
327: List allNodes = root.preOrder();
328: List documents = new ArrayList();
329:
330: for (int i = 1; i < allNodes.size(); i++) {
331: SiteTreeNode node = (SiteTreeNode) allNodes.get(i);
332: Document doc = map.get(publication, area, node
333: .getUuid());
334: String[] languages = doc.getLanguages();
335: for (int l = 0; l < languages.length; l++) {
336: documents.add(doc.getTranslation(languages[l]));
337: }
338: }
339: return (Document[]) documents
340: .toArray(new Document[documents.size()]);
341: } catch (Exception e) {
342: throw new SiteException(e);
343: }
344: }
345:
346: public void add(String path, Document document)
347: throws SiteException {
348:
349: if (contains(document)) {
350: throw new SiteException("The document [" + document
351: + "] is already contained!");
352: }
353: DefaultSiteTree tree = getTree(document);
354:
355: SiteTreeNode node;
356: if (!tree.contains(path)) {
357: // done for side effect of calling addNodNode, not the resulting return value
358: node = tree.addNode(path, document.getUUID(), true, null,
359: null, false);
360: } else {
361: node = (SiteTreeNode) tree.getNode(path);
362: if (node.getUuid() != null) {
363: ((SiteTreeNodeImpl) node).setUUID(document.getUUID());
364: }
365: }
366: tree.addLabel(path, document.getLanguage(), "");
367: }
368:
369: public void set(String path, Document document)
370: throws SiteException {
371:
372: if (contains(document)) {
373: throw new SiteException("The document [" + document
374: + "] is already contained!");
375: }
376: DefaultSiteTree tree = getTree(document);
377: SiteTreeNodeImpl node = (SiteTreeNodeImpl) tree.getNode(path);
378: node.setUUID(document.getUUID());
379: tree.changed();
380: }
381:
382: /**
383: * @param publication The publication.
384: * @param area The area.
385: * @return The key to store sitetree objects in the identity map.
386: */
387: protected String getKey(Publication publication, String area) {
388: return publication.getId() + ":" + area;
389: }
390:
391: /**
392: * @see org.apache.lenya.cms.site.SiteManager#getSiteStructure(org.apache.lenya.cms.publication.DocumentFactory,
393: * org.apache.lenya.cms.publication.Publication, java.lang.String)
394: */
395: public SiteStructure getSiteStructure(DocumentFactory map,
396: Publication publiation, String area) throws SiteException {
397: return getTree(map, publiation, area);
398: }
399:
400: /**
401: * @see org.apache.lenya.cms.site.SiteManager#getAvailableLocator(DocumentFactory,
402: * org.apache.lenya.cms.publication.DocumentLocator)
403: */
404: public DocumentLocator getAvailableLocator(DocumentFactory factory,
405: DocumentLocator locator) throws SiteException {
406: return DocumentLocator.getLocator(locator.getPublicationId(),
407: locator.getArea(), computeUniquePath(factory, locator),
408: locator.getLanguage());
409: }
410:
411: /**
412: * compute an unique document id
413: * @param factory The factory.
414: * @param locator The locator.
415: * @return the unique documentid
416: * @throws SiteException if an error occurs.
417: */
418: protected String computeUniquePath(DocumentFactory factory,
419: DocumentLocator locator) throws SiteException {
420: String path = locator.getPath();
421:
422: Publication pub;
423: try {
424: pub = factory.getPublication(locator.getPublicationId());
425: } catch (PublicationException e) {
426: throw new SiteException(e);
427: }
428: SiteTree tree = getTree(factory, pub, locator.getArea());
429:
430: String suffix = null;
431: int version = 0;
432: String idwithoutsuffix = null;
433:
434: if (tree.contains(path)) {
435: int n = path.lastIndexOf("/");
436: String lastToken = "";
437: String substring = path;
438: if ((n < path.length()) && (n > 0)) {
439: lastToken = path.substring(n);
440: substring = path.substring(0, n);
441: }
442:
443: int l = lastToken.length();
444: int index = lastToken.lastIndexOf("-");
445: if (0 < index && index < l) {
446: suffix = lastToken.substring(index + 1);
447: idwithoutsuffix = substring
448: + lastToken.substring(0, index);
449: version = Integer.parseInt(suffix);
450: } else {
451: idwithoutsuffix = substring + lastToken;
452: }
453:
454: while (tree.contains(path)) {
455: version = version + 1;
456: path = idwithoutsuffix + "-" + version;
457: }
458: }
459:
460: return path;
461: }
462:
463: public boolean isVisibleInNav(Document document)
464: throws SiteException {
465: SiteTree tree = getTree(document);
466: try {
467: return tree.getNode(document.getPath()).isVisible();
468: } catch (DocumentException e) {
469: throw new SiteException(e);
470: }
471: }
472:
473: protected String getPath(DocumentFactory factory, Publication pub,
474: String area, String uuid, String language)
475: throws SiteException {
476: SiteTree tree = getTree(factory, pub, area);
477: SiteNode node = tree.getByUuid(uuid, language).getNode();
478: if (node == null) {
479: throw new SiteException("No node found for [" + pub.getId()
480: + ":" + area + ":" + uuid + ":" + language + "]");
481: }
482: return node.getPath();
483: }
484:
485: protected String getUUID(DocumentFactory factory, Publication pub,
486: String area, String path) throws SiteException {
487: SiteTree tree = getTree(factory, pub, area);
488: SiteNode node = tree.getNode(path);
489: if (node == null) {
490: throw new SiteException("No node found for [" + pub.getId()
491: + ":" + area + ":" + path + "]");
492: }
493: return node.getUuid();
494: }
495:
496: protected boolean contains(DocumentFactory factory,
497: DocumentLocator locator) throws SiteException {
498: Publication pub;
499: try {
500: pub = factory.getPublication(locator.getPublicationId());
501: } catch (PublicationException e) {
502: throw new SiteException(e);
503: }
504: SiteTree tree = getTree(factory, pub, locator.getArea());
505: if (tree.contains(locator.getPath())) {
506: SiteNode node = tree.getNode(locator.getPath());
507: return node.hasLink(locator.getLanguage());
508: } else {
509: return false;
510: }
511: }
512:
513: }
|