- add moved class to core-sitemanagement ; extract classes related to metadatas
git-svn-id: https://svn.apache.org/repos/asf/lenya/trunk@1034333 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/metadata/usecases/Metadata.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/metadata/usecases/Metadata.java
deleted file mode 100644
index fddbff6..0000000
--- a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/metadata/usecases/Metadata.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- *
- */
-package org.apache.lenya.cms.metadata.usecases;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.lenya.cms.metadata.Element;
-import org.apache.lenya.cms.metadata.MetaData;
-import org.apache.lenya.cms.metadata.MetaDataRegistry;
-import org.apache.lenya.cms.publication.Document;
-import org.apache.lenya.cms.publication.Node;
-import org.apache.lenya.cms.site.usecases.SiteUsecase;
-import org.apache.lenya.cms.usecase.UsecaseException;
-import org.apache.lenya.cms.workflow.WorkflowUtil;
-
-/**
- * Usecase to edit metadata for a resource.
- *
- * @version $Id$
- */
-public class Metadata extends SiteUsecase {
-
- private MetaDataRegistry metaDataRegistry;
-
- /**
- * @see org.apache.lenya.cms.usecase.AbstractUsecase#getNodesToLock()
- */
- protected Node[] getNodesToLock() throws UsecaseException {
- Node[] objects = new Node[0];
- if(getSourceDocument() != null) {
- objects = new Node[] { getSourceDocument() };
- }
- return objects;
- }
-
- /**
- * Object to pass a meta data entry to the view.
- */
- public static class MetaDataWrapper {
-
- private String[] values;
- private Element element;
- private boolean editable;
-
- /**
- * @param element The element.
- * @param values The values for the element.
- * @param canChange If the element value can be changed via the GUI. A <code>true</code>
- * value is only effective if the element itself is editable.
- */
- public MetaDataWrapper(Element element, String[] values, boolean canChange) {
- this.values = values;
- this.element = element;
- this.editable = element.isEditable() && canChange;
- }
-
- /**
- * @return The values for the element.
- */
- public String[] getValues() {
- return this.values;
- }
-
- /**
- * @return The element.
- */
- public Element getElement() {
- return this.element;
- }
-
- /**
- * @return If the value can be changed via the GUI.
- */
- public boolean isEditable() {
- return this.editable;
- }
-
- }
-
- /**
- * @see org.apache.lenya.cms.usecase.AbstractUsecase#initParameters()
- */
- protected void initParameters() {
- super.initParameters();
-
- Document doc = getSourceDocument();
- if (doc == null) {
- return;
- }
-
-
- try {
- boolean canChange = WorkflowUtil.canInvoke(doc, "edit");
-
- if (!canChange) {
- addInfoMessage("cannot-change-metadata");
- }
-
- List numbers = new ArrayList();
- Map num2namespace = new HashMap();
- List keyList = new ArrayList();
-
- String[] namespaces = getMetaDataRegistry().getNamespaceUris();
-
- for (int nsIndex = 0; nsIndex < namespaces.length; nsIndex++) {
- MetaData meta = doc.getMetaData(namespaces[nsIndex]);
- String[] keys = meta.getPossibleKeys();
- for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
- String key = "ns" + nsIndex + "." + keys[keyIndex];
- String[] values = meta.getValues(keys[keyIndex]);
- Element element = meta.getElementSet().getElement(keys[keyIndex]);
- setParameter(key, new MetaDataWrapper(element, values, canChange));
- keyList.add(key);
- }
- numbers.add("" + nsIndex);
- num2namespace.put("" + nsIndex, namespaces[nsIndex]);
- }
-
- setParameter("numbers", numbers);
- setParameter("namespaces", num2namespace);
-
- Collections.sort(keyList);
- setParameter("keys", keyList);
-
- } catch (Exception e) {
- getLogger().error("Unable to load meta data.", e);
- addErrorMessage("Unable to load meta data: " + e.getMessage());
- }
- }
-
- protected MetaDataRegistry getMetaDataRegistry() {
- return metaDataRegistry;
- }
-
- public void setMetaDataRegistry(MetaDataRegistry metaDataRegistry) {
- this.metaDataRegistry = metaDataRegistry;
- }
-
-}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractLink.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractLink.java
new file mode 100644
index 0000000..fe01f12
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractLink.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* @version $Id$ */
+
+package org.apache.lenya.cms.site;
+
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Publication;
+
+/**
+ * The AbstractLink class encapsulates a string label and a associated language.
+ */
+public abstract class AbstractLink implements Link {
+ private String label = null;
+ private String language = null;
+
+ /**
+ * Creates a new AbstractLink object.
+ * @param node The site node.
+ * @param _label the actual label
+ * @param _language the language
+ */
+ public AbstractLink(SiteNode node, String _label, String _language) {
+ this.label = _label;
+ this.language = _language;
+ this.node = node;
+ }
+
+ /**
+ * Get the actual label of the AbstractLink object
+ *
+ * @return the actual label as a String
+ */
+ public String getLabel() {
+ return this.label;
+ }
+
+ /**
+ * Get the language of this AbstractLink object
+ *
+ * @return the language
+ */
+
+ public String getLanguage() {
+ return this.language;
+ }
+
+ /**
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return getLabel() + " " + getLanguage();
+ }
+
+ /**
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ boolean equals = false;
+
+ if (getClass().isInstance(obj)) {
+ AbstractLink otherLabel = (AbstractLink) obj;
+ equals = getLabel().equals(otherLabel.getLabel())
+ && getLanguage().equals(otherLabel.getLanguage());
+ }
+
+ return equals;
+ }
+
+ /**
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return getLabel().hashCode() + getLanguage().hashCode();
+ }
+
+ private SiteNode node;
+
+ public Document getDocument() {
+ SiteNode node = getNode();
+ String uuid = node.getUuid();
+ if (uuid == null) {
+ throw new UnsupportedOperationException("The node [" + node + "] has no UUID.");
+ }
+ Publication pub = node.getStructure().getPublication();
+ String area = node.getStructure().getArea();
+ return pub.getArea(area).getDocument(uuid, getLanguage());
+ }
+
+ public SiteNode getNode() {
+ return this.node;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ save();
+ }
+
+ protected void save() {
+ }
+
+}
\ No newline at end of file
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteManager.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteManager.java
new file mode 100644
index 0000000..0f2f784
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteManager.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.site;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.cocoon.util.AbstractLogEnabled;
+
+/**
+ * Abstract base class for site managers.
+ *
+ * @version $Id$
+ */
+public abstract class AbstractSiteManager extends AbstractLogEnabled implements SiteManager {
+
+ /**
+ * @see org.apache.lenya.cms.site.SiteManager#sortAscending(org.apache.lenya.cms.publication.util.DocumentSet)
+ */
+ public SiteNode[] sortAscending(SiteNode[] nodes) throws SiteException {
+ if (nodes.length > 0) {
+
+ if (!check(new NodeSet(nodes))) {
+ throw new SiteException("The dependence relation is not a strict partial order!");
+ }
+
+ SiteNode[] sortedNodes = (SiteNode[]) Arrays.asList(nodes).toArray(
+ new SiteNode[nodes.length]);
+ Arrays.sort(sortedNodes, new NodeComparator());
+ return sortedNodes;
+ } else {
+ return nodes;
+ }
+ }
+
+ /**
+ * Checks if the dependence relation is a strict partial order.
+ *
+ * @param set The document set to check.
+ * @return A boolean value.
+ * @throws SiteException when something went wrong.
+ */
+ protected boolean check(NodeSet set) throws SiteException {
+ boolean isStrictPartialOrder = isIrreflexive(set) && isAntisymmetric(set)
+ && isTransitive(set);
+ return isStrictPartialOrder;
+ }
+
+ /**
+ * Checks if the dependence relation is antisymmetric.
+ *
+ * @param set The document set to check.
+ * @return A boolean value.
+ * @throws SiteException when something went wrong.
+ */
+ protected boolean isAntisymmetric(NodeSet set) throws SiteException {
+ SiteNode[] resources = set.getNodes();
+ boolean isAntisymmetric = true;
+ for (int i = 0; i < resources.length; i++) {
+ for (int j = i + 1; j < resources.length; j++) {
+ if (requires(resources[i], resources[j]) && requires(resources[j], resources[i])
+ && !(resources[i] == resources[j])) {
+ isAntisymmetric = false;
+ }
+ }
+ }
+ return isAntisymmetric;
+ }
+
+ /**
+ * Checks if the dependence relation is transitive.
+ *
+ * @param set The document set to check.
+ * @return A boolean value.
+ * @throws SiteException when something went wrong.
+ */
+ protected boolean isTransitive(NodeSet set) throws SiteException {
+ SiteNode[] resources = set.getNodes();
+ boolean isTransitive = true;
+ for (int i = 0; i < resources.length; i++) {
+ for (int j = i + 1; j < resources.length; j++) {
+ for (int k = j + 1; k < resources.length; k++) {
+ if (requires(resources[i], resources[j])
+ && requires(resources[j], resources[k])
+ && !requires(resources[i], resources[k])) {
+ isTransitive = false;
+ }
+ }
+ }
+ }
+ return isTransitive;
+ }
+
+ /**
+ * Checks if the dependence relation is irreflexive.
+ *
+ * @param set The document set.
+ * @return A boolean value
+ * @throws SiteException
+ */
+ protected boolean isIrreflexive(NodeSet set) throws SiteException {
+ SiteNode[] resources = set.getNodes();
+ boolean isIrreflexive = true;
+ for (int i = 0; i < resources.length; i++) {
+ if (requires(resources[i], resources[i])) {
+ isIrreflexive = false;
+ }
+ }
+ return isIrreflexive;
+ }
+
+ /**
+ * Compares nodes according to the dependence relation.
+ */
+ public class NodeComparator implements Comparator {
+
+ /**
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(Object arg0, Object arg1) {
+ int result = 0;
+ if (arg0 instanceof SiteNode && arg1 instanceof SiteNode) {
+ SiteNode doc1 = (SiteNode) arg0;
+ SiteNode doc2 = (SiteNode) arg1;
+
+ try {
+ if (AbstractSiteManager.this.requires(doc1, doc2)) {
+ result = 1;
+ } else if (AbstractSiteManager.this.requires(doc2, doc1)) {
+ result = -1;
+ }
+ } catch (SiteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteNode.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteNode.java
new file mode 100644
index 0000000..bcd161f
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/AbstractSiteNode.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import java.util.Arrays;
+
+import org.apache.avalon.framework.container.ContainerUtil;
+import org.apache.cocoon.util.AbstractLogEnabled;
+import org.apache.commons.lang.Validate;
+import org.apache.commons.logging.Log;
+import org.apache.lenya.cms.publication.Publication;
+
+/**
+ * Abstract site node implementation.
+ */
+public abstract class AbstractSiteNode extends AbstractLogEnabled implements SiteNode {
+
+ private String path;
+ private SiteStructure structure;
+ private String uuid;
+
+ protected AbstractSiteNode(Publication publication, SiteStructure structure, String path,
+ String uuid, Log logger)
+ {
+ Validate.notNull(structure);
+ Validate.notNull(path);
+ Validate.isTrue(path.startsWith("/"), "Path must start with /");
+ Validate.notNull(uuid);
+ this.structure = structure;
+ this.path = path;
+ this.uuid = uuid;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public SiteStructure getStructure() {
+ return this.structure;
+ }
+
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof SiteNode)) {
+ return false;
+ }
+ String thisKey = getKey(getStructure().getPublication(),
+ getStructure().getArea(),
+ getPath());
+ SiteNode node = (SiteNode) obj;
+ String nodeKey = getKey(node.getStructure().getPublication(),
+ node.getStructure().getArea(),
+ node.getPath());
+ return thisKey.equals(nodeKey);
+ }
+
+ public int hashCode() {
+ return getKey(getStructure().getPublication(), getStructure().getArea(), getPath()).hashCode();
+ }
+
+ protected static String getKey(Publication pub, String area, String docId) {
+ return pub.getId() + ":" + area + ":" + docId;
+ }
+
+ public SiteNode getParent() throws SiteException {
+ String id = getPath().substring(1);
+ String[] steps = id.split("/");
+ if (steps.length == 1) {
+ throw new SiteException("The node [" + getPath() + "] is a top-level node.");
+ } else {
+ int lastIndex = id.lastIndexOf("/");
+ String parentId = id.substring(0, lastIndex);
+ return getStructure().getNode("/" + parentId);
+ }
+ }
+
+ public boolean isTopLevel() {
+ return getPath().lastIndexOf("/") == 0;
+ }
+
+ public String getUuid() {
+ return this.uuid;
+ }
+
+ public boolean hasLink(String language) {
+ return Arrays.asList(getLanguages()).contains(language);
+ }
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/Link.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/Link.java
new file mode 100644
index 0000000..d7e3f14
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/Link.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import org.apache.lenya.cms.publication.Document;
+
+/**
+ * A link in the site structure references a document.
+ * A site node contains a link for each translation.
+ */
+public interface Link {
+
+ /**
+ * @return The language of this link.
+ */
+ String getLanguage();
+
+ /**
+ * @return The document this link points to.
+ */
+ Document getDocument();
+
+ /**
+ * @return The node this link belongs to.
+ */
+ SiteNode getNode();
+
+ /**
+ * @return The label of this link.
+ */
+ String getLabel();
+
+ /**
+ * @param label The new label.
+ */
+ void setLabel(String label);
+
+ /**
+ * Removes the link.
+ */
+ void delete();
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeIterator.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeIterator.java
new file mode 100644
index 0000000..9806b82
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeIterator.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.lang.Validate;
+
+/**
+ * Node iterator.
+ */
+public class NodeIterator {
+
+ private Iterator delegate;
+
+ /**
+ * @param collection The collection to iterate over.
+ */
+ public NodeIterator(Collection collection) {
+ Validate.notNull(collection);
+ this.delegate = collection.iterator();
+ }
+
+ /**
+ * @param nodes The nodes to iterate over.
+ */
+ public NodeIterator(SiteNode[] nodes) {
+ Validate.notNull(nodes);
+ this.delegate = Arrays.asList(nodes).iterator();
+ }
+
+ /**
+ * @return A site node.
+ * @see Iterator#next()
+ */
+ public SiteNode next() {
+ return (SiteNode) this.delegate.next();
+ }
+
+ /**
+ * @return A boolean value.
+ * @see Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ return this.delegate.hasNext();
+ }
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeSet.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeSet.java
new file mode 100644
index 0000000..c0ee4cd
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/NodeSet.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentException;
+import org.apache.lenya.cms.publication.util.DocumentSet;
+
+/**
+ * A set containing nodes.
+ */
+public class NodeSet {
+
+ /**
+ * Ctor.
+ * @param manager The service manager.
+ */
+ public NodeSet() {
+ }
+
+ /**
+ * Ctor.
+ * @param _nodes The initial nodes.
+ */
+ public NodeSet(SiteNode[] _nodes) {
+ for (int i = 0; i < _nodes.length; i++) {
+ add(_nodes[i]);
+ }
+ }
+
+ /**
+ * Ctor.
+ * @param documents The corresponding documents to derive nodes from.
+ */
+ public NodeSet(DocumentSet documents) {
+ Document[] docs = documents.getDocuments();
+ for (int i = 0; i < docs.length; i++) {
+ SiteNode node;
+ try {
+ node = docs[i].getLink().getNode();
+ } catch (DocumentException e) {
+ throw new RuntimeException(e);
+ }
+ if (!contains(node)) {
+ add(node);
+ }
+ }
+ }
+
+ /**
+ * @param node A node.
+ * @return If the node is contained.
+ */
+ public boolean contains(SiteNode node) {
+ return getSet().contains(node);
+ }
+
+ private Set nodes = new HashSet();
+
+ /**
+ * Returns the list object that stores the documents.
+ * @return A list.
+ */
+ protected Set getSet() {
+ return this.nodes;
+ }
+
+ /**
+ * Returns the documents contained in this set.
+ *
+ * @return An array of documents.
+ */
+ public SiteNode[] getNodes() {
+ return (SiteNode[]) this.nodes.toArray(new SiteNode[this.nodes.size()]);
+ }
+
+ /**
+ * Adds a node to this set.
+ * @param node The node to add.
+ */
+ public void add(SiteNode node) {
+ assert node != null;
+ assert !this.nodes.contains(node);
+ this.nodes.add(node);
+ }
+
+ /**
+ * Checks if this set is empty.
+ * @return A boolean value.
+ */
+ public boolean isEmpty() {
+ return getSet().isEmpty();
+ }
+
+ /**
+ * Removes a node.
+ * @param resource The node.
+ */
+ public void remove(SiteNode resource) {
+ assert resource != null;
+ assert getSet().contains(resource);
+ getSet().remove(resource);
+ }
+
+ /**
+ * Removes all nodes.
+ */
+ public void clear() {
+ getSet().clear();
+ }
+
+ /**
+ * @return An iterator iterating in undetermined order.
+ */
+ public NodeIterator iterator() {
+ return new NodeIterator(getNodes());
+ }
+
+ /**
+ * @return An iterator iterating in ascending order.
+ */
+ public NodeIterator ascending() {
+ SiteNode[] nodes = getNodesAscending();
+ return new NodeIterator(nodes);
+ }
+
+ /**
+ * @return An iterator iterating in descending order.
+ */
+ public NodeIterator descending() {
+ SiteNode[] nodes = getNodesAscending();
+ List list = Arrays.asList(nodes);
+ Collections.reverse(list);
+ return new NodeIterator(list);
+ }
+
+ protected SiteNode[] getNodesAscending() {
+ if (isEmpty()) {
+ return new SiteNode[0];
+ }
+
+ try {
+ String hint = getNodes()[0].getStructure().getPublication().getSiteManagerHint();
+ SiteManager siteManager = (SiteManager) WebAppContextUtils.getCurrentWebApplicationContext().getBean(
+ SiteManager.class.getName() + "/" + hint);
+ return siteManager.sortAscending(getNodes());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @return All documents referenced by this node set.
+ */
+ public Document[] getDocuments() {
+ List documents = new ArrayList();
+ for (NodeIterator i = iterator(); i.hasNext();) {
+ SiteNode node = i.next();
+ String[] langs = node.getLanguages();
+ for (int l = 0; l < langs.length; l++) {
+ try {
+ documents.add(node.getLink(langs[l]).getDocument());
+ } catch (SiteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return (Document[]) documents.toArray(new Document[documents.size()]);
+ }
+
+ /**
+ * Adds all nodes from a node set to this.
+ * @param set The set.
+ */
+ public void addAll(NodeSet set) {
+ this.nodes.addAll(set.getSet());
+ }
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteException.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteException.java
new file mode 100644
index 0000000..a0acfc3
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteException.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lenya.cms.site;
+
+import org.apache.lenya.cms.publication.PublicationException;
+
+/**
+ * Site structure management exception.
+ *
+ * @version $Id$
+ */
+public class SiteException extends PublicationException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Ctor.
+ */
+ public SiteException() {
+ super();
+ }
+
+ /**
+ * Ctor.
+ * @param message The message.
+ */
+ public SiteException(String message) {
+ super(message);
+ }
+
+ /**
+ * Ctor.
+ * @param cause The cause.
+ */
+ public SiteException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Ctor.
+ * @param message The message.
+ * @param cause The cause.
+ */
+ public SiteException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteManager.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteManager.java
new file mode 100644
index 0000000..a1c83e3
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteManager.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.lenya.cms.site;
+
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentLocator;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * <p>
+ * A site structure management component.
+ * </p>
+ *
+ * <p>
+ * A site manager has a dependence relation, which is always applied to documents of a single
+ * language. This means a document may not require a document of another language. Dependence on a
+ * set of resources must be a strict partial order <strong>< </strong>:
+ * </p>
+ * <ul>
+ * <li><em>irreflexive:</em> d <strong>< </strong>d does not hold for any resource d</li>
+ * <li><em>antisymmetric:</em> d <strong>< </strong>e and e <strong>< </strong>d implies d=e</li>
+ * <li><em>transitive:</em> d <strong>< </strong>e and e <strong>< </strong>f implies d
+ * <strong>< </strong>f</li>
+ * </ul>
+ *
+ * @version $Id$
+ */
+public interface SiteManager {
+
+ /**
+ * The Avalon role.
+ */
+ String ROLE = SiteManager.class.getName();
+
+ /**
+ * Checks if a resource requires another one.
+ * @param dependingResource The depending resource.
+ * @param requiredResource The required resource.
+ * @return A boolean value.
+ * @throws SiteException if an error occurs.
+ */
+ boolean requires(SiteNode dependingResource, SiteNode requiredResource) throws SiteException;
+
+ /**
+ * Returns the resources which are required by a certain resource.
+ *
+ * @param session The session to operate on.
+ * @param locator The depending locator.
+ * @return An array of resources.
+ * @throws SiteException if an error occurs.
+ */
+ DocumentLocator[] getRequiredResources(Session session, DocumentLocator locator)
+ throws SiteException;
+
+ /**
+ * Returns the resources which require a certain resource.
+ *
+ * @param resource The required resource.
+ * @return An array of resources.
+ * @throws SiteException if an error occurs.
+ */
+ SiteNode[] getRequiringResources(SiteNode resource) throws SiteException;
+
+ /**
+ * Adds a document to the site structure.
+ * @param path The path.
+ * @param document The document to add.
+ * @throws SiteException if the document is already contained.
+ */
+ void add(String path, Document document) throws SiteException;
+
+ /**
+ * Sets a document to the site structure.
+ * @param path The path.
+ * @param document The document to add.
+ * @throws SiteException if the document is already contained or if the path doesn't exist.
+ */
+ void set(String path, Document document) throws SiteException;
+
+ /**
+ * Checks if the site structure contains a certain resource in a certain area.
+ *
+ * @param resource The resource.
+ * @return A boolean value.
+ * @throws SiteException if an error occurs.
+ */
+ boolean contains(Document resource) throws SiteException;
+
+ /**
+ * Checks if the site structure contains any language version of a certain resource in a certain
+ * area.
+ *
+ * @param resource The resource.
+ * @return A boolean value.
+ * @throws SiteException if an error occurs.
+ */
+ boolean containsInAnyLanguage(Document resource) throws SiteException;
+
+ /**
+ * Copies a document in the site structure.
+ *
+ * @param sourceDocument The source document.
+ * @param destinationDocument The destination document.
+ * @throws SiteException when something went wrong.
+ */
+ void copy(Document sourceDocument, Document destinationDocument) throws SiteException;
+
+ /**
+ * Sets the visibility of a node in the navigation. It is meant to hide specific nodes within
+ * the "public" navigation whereas the node is visible within the info/site area.
+ *
+ * @param document The document.
+ * @param visibleInNav The visibility.
+ * @throws SiteException if an error occurs.
+ */
+ void setVisibleInNav(Document document, boolean visibleInNav) throws SiteException;
+
+ /**
+ * Returns the visibility of a node in the navigation.
+ *
+ * @param document The document.
+ * @return A boolean value.
+ * @throws SiteException if an error occurs.
+ */
+ boolean isVisibleInNav(Document document) throws SiteException;
+
+ /**
+ * Returns all documents in a certain area.
+ *
+ * @param publication The publication.
+ * @param area The area.
+ * @return An array of documents.
+ * @throws SiteException if an error occurs.
+ */
+ Document[] getDocuments(Publication publication, String area) throws SiteException;
+
+ /**
+ * Sorts a set of nodes using the "requires" relation.
+ *
+ * @param nodes The set.
+ * @return A sorted array of nodes.
+ * @throws SiteException if an error occurs.
+ */
+ SiteNode[] sortAscending(SiteNode[] nodes) throws SiteException;
+
+ /**
+ * @param publication The publication.
+ * @param area The area.
+ * @return The object that holds the site structure information.
+ * @throws SiteException if an error occurs.
+ */
+ SiteStructure getSiteStructure(Publication publication, String area) throws SiteException;
+
+ /**
+ * Checks if the document does already exist. If it does, returns a non-existing document with a
+ * similar document ID. If it does not, the original document is returned.
+ * @param session The session.
+ * @param locator The locator.
+ * @return A locator.
+ * @throws SiteException if the new document could not be built.
+ */
+ DocumentLocator getAvailableLocator(Session session, DocumentLocator locator)
+ throws SiteException;
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteNode.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteNode.java
new file mode 100644
index 0000000..af9936c
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteNode.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+/**
+ * A node in the site structure.
+ */
+public interface SiteNode {
+
+ /**
+ * @return The site structure this node belongs to.
+ */
+ SiteStructure getStructure();
+
+ /**
+ * @return The path.
+ */
+ String getPath();
+
+ /**
+ * @return The parent node.
+ * @throws SiteException If the node has no parent.
+ */
+ SiteNode getParent() throws SiteException;
+
+ /**
+ * @return If this is a top level node. Top level nodes have no parents.
+ */
+ boolean isTopLevel();
+
+ /**
+ * @return The languages of this node.
+ */
+ String[] getLanguages();
+
+ /**
+ * @param language The language.
+ * @return The link for the language.
+ * @throws SiteException if no link is contained for the language.
+ */
+ Link getLink(String language) throws SiteException;
+
+ /**
+ * @return The UUID of this node.
+ */
+ String getUuid();
+
+ /**
+ * Checks if a link for a certain language is contained.
+ * @param language The language.
+ * @return A boolean value.
+ */
+ boolean hasLink(String language);
+
+ /**
+ * @return The name, i.e. the last path element.
+ */
+ String getName();
+
+ /**
+ * @return if the node is visible in the navigation.
+ */
+ boolean isVisible();
+
+ /**
+ * Sets the node visibility in the navigation.
+ * @param visibleInNav if the node should be visible.
+ */
+ void setVisible(boolean visibleInNav);
+
+ /**
+ * Deletes this node.
+ */
+ void delete();
+
+ /**
+ * @return The children of this node.
+ */
+ SiteNode[] getChildren();
+
+ /**
+ * @return if the node has an external link.
+ */
+ boolean hasLink();
+
+ /**
+ * @return The external link.
+ */
+ String getHref();
+
+ /**
+ * @return The suffix.
+ */
+ String getSuffix();
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteStructure.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteStructure.java
new file mode 100644
index 0000000..dc7f3cb
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteStructure.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.Node;
+import org.apache.lenya.cms.publication.Publication;
+
+/**
+ * Object to hold a site structure information.
+ *
+ * @version $Id$
+ */
+public interface SiteStructure extends Node {
+
+ /**
+ * @return The publication.
+ */
+ Publication getPublication();
+
+ /**
+ * @return The area.
+ */
+ String getArea();
+
+ /**
+ * @return All nodes in this structure.
+ */
+ SiteNode[] getNodes();
+
+ /**
+ * @param path The path.
+ * @return A site node.
+ * @throws SiteException if no node is contained for the path.
+ */
+ SiteNode getNode(String path) throws SiteException;
+
+ /**
+ * Checks if a node is contained for a certain path.
+ * @param path The path.
+ * @return A boolean value.
+ */
+ boolean contains(String path);
+
+ /**
+ * Checks if a link is contained for a certain path and language.
+ * @param path The path.
+ * @param language The language.
+ * @return A boolean value.
+ */
+ boolean contains(String path, String language);
+
+ /**
+ * Checks if the structure contains a link with a certain UUID and language.
+ * @param uuid The UUID.
+ * @param language The language.
+ * @return A boolean value.
+ */
+ boolean containsByUuid(String uuid, String language);
+
+ /**
+ * Checks if the structure contains any language version of a document.
+ * @param uuid The uuid.
+ * @return A boolean value.
+ */
+ boolean containsInAnyLanguage(String uuid);
+
+ /**
+ * Returns a node for a certain UUID.
+ * @param uuid The UUID.
+ * @param language The language.
+ * @return a link.
+ * @throws SiteException if no node is contained for the UUID.
+ */
+ Link getByUuid(String uuid, String language) throws SiteException;
+
+ /**
+ * Adds a link to a document.
+ * @param path The path.
+ * @param doc The document.
+ * @return A link.
+ * @throws SiteException if the document is already contained or the node
+ * for this path already contains a link for this language.
+ */
+ Link add(String path, Document doc) throws SiteException;
+
+ /**
+ * Adds a site node.
+ * @param path The path.
+ * @return A site node.
+ * @throws SiteException if the path is already contained.
+ */
+ SiteNode add(String path) throws SiteException;
+
+ /**
+ * Adds a site node before a specific other node.
+ * @param path The path.
+ * @param followingSiblingPath The path of the node which will be the
+ * following sibling of the node to insert.
+ * @return A site node.
+ * @throws SiteException if the path is already contained.
+ */
+ SiteNode add(String path, String followingSiblingPath) throws SiteException;
+
+ /**
+ * @return The top level nodes.
+ */
+ SiteNode[] getTopLevelNodes();
+
+}
diff --git a/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteUtil.java b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteUtil.java
new file mode 100644
index 0000000..53a27d7
--- /dev/null
+++ b/org.apache.lenya.core.sitemanagement/src/main/java/org/apache/lenya/cms/site/SiteUtil.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.lenya.cms.site;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.cocoon.spring.configurator.WebAppContextUtils;
+import org.apache.lenya.cms.publication.DocumentLocator;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.Session;
+
+/**
+ * Utility to handle site structures.
+ *
+ * @version $Id$
+ */
+public class SiteUtil {
+
+ private SiteUtil() {
+ }
+
+ /**
+ * Returns a sub-site starting with a certain node, which includes the node itself and all nodes
+ * which require this node, in preorder.
+ *
+ * @param node The top-level document.
+ * @return A document set.
+ * @throws SiteException if an error occurs.
+ */
+ public static NodeSet getSubSite(SiteNode node) throws SiteException {
+ SiteManager siteManager = null;
+ SiteNode[] subsite;
+ try {
+ String hint = node.getStructure().getPublication().getSiteManagerHint();
+ siteManager = (SiteManager) WebAppContextUtils.getCurrentWebApplicationContext()
+ .getBean(SiteManager.class.getName() + "/" + hint);
+
+ Set nodes = new HashSet();
+ nodes.add(node);
+
+ SiteNode[] requiringNodes = siteManager.getRequiringResources(node);
+ for (int i = 0; i < requiringNodes.length; i++) {
+ nodes.add(requiringNodes[i]);
+ }
+
+ subsite = (SiteNode[]) nodes.toArray(new SiteNode[nodes.size()]);
+ } catch (Exception e) {
+ throw new SiteException(e);
+ }
+ return new NodeSet(subsite);
+ }
+
+ /**
+ * @see org.apache.lenya.cms.site.SiteManager#getAvailableLocator(DocumentFactory,
+ * DocumentLocator)
+ * @param factory The factory.
+ * @param locator The locator.
+ * @return A document.
+ * @throws SiteException if an error occurs.
+ */
+ public static DocumentLocator getAvailableLocator(Session session,
+ DocumentLocator locator) throws SiteException {
+ SiteManager siteManager = null;
+ try {
+ Publication pub = session.getPublication(locator.getPublicationId());
+ siteManager = (SiteManager) WebAppContextUtils.getCurrentWebApplicationContext()
+ .getBean(SiteManager.ROLE + "/" + pub.getSiteManagerHint());
+ return siteManager.getAvailableLocator(session, locator);
+ } catch (Exception e) {
+ throw new SiteException(e);
+ }
+ }
+
+}
\ No newline at end of file