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: package org.apache.lenya.modules.collection;
019:
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.List;
024:
025: import javax.xml.parsers.ParserConfigurationException;
026: import javax.xml.transform.TransformerException;
027:
028: import org.apache.avalon.framework.logger.AbstractLogEnabled;
029: import org.apache.avalon.framework.logger.Logger;
030: import org.apache.avalon.framework.service.ServiceException;
031: import org.apache.lenya.cms.publication.Document;
032: import org.apache.lenya.cms.publication.DocumentBuildException;
033: import org.apache.lenya.cms.publication.DocumentException;
034: import org.apache.lenya.xml.DocumentHelper;
035: import org.apache.lenya.xml.NamespaceHelper;
036: import org.apache.xpath.XPathAPI;
037: import org.w3c.dom.DOMException;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.Node;
040: import org.w3c.dom.NodeList;
041: import org.xml.sax.SAXException;
042:
043: /**
044: * Document wrapper for collection functionality.
045: */
046: public class CollectionWrapper extends AbstractLogEnabled implements
047: Collection {
048:
049: private Document delegate;
050:
051: protected static final String[] TYPES = { TYPE_CHILDREN,
052: TYPE_MANUAL, TYPE_LINK };
053:
054: /**
055: * Ctor.
056: * @param doc The document.
057: * @param logger The logger.
058: */
059: public CollectionWrapper(Document doc, Logger logger) {
060: enableLogging(logger);
061: this .delegate = doc;
062: }
063:
064: public Document getDelegate() {
065: return this .delegate;
066: }
067:
068: private List documentsList;
069:
070: /**
071: * Returns the list that holds the documents. Use this method to invoke lazy
072: * loading.
073: * @return A list.
074: * @throws DocumentException when something went wrong.
075: */
076: protected List documents() throws DocumentException {
077: load();
078: return this .documentsList;
079: }
080:
081: /**
082: * @see org.apache.lenya.modules.collection.Collection#getDocuments()
083: */
084: public Document[] getDocuments() throws DocumentException {
085: return (Document[]) documents().toArray(
086: new Document[documents().size()]);
087: }
088:
089: /**
090: * @see org.apache.lenya.modules.collection.Collection#add(org.apache.lenya.cms.publication.Document)
091: */
092: public void add(Document document) throws DocumentException {
093: documents().add(document);
094: }
095:
096: /**
097: * @see org.apache.lenya.modules.collection.Collection#add(int,
098: * org.apache.lenya.cms.publication.Document)
099: */
100: public void add(int position, Document document)
101: throws DocumentException {
102: documents().add(position, document);
103: }
104:
105: /**
106: * @see org.apache.lenya.modules.collection.Collection#remove(org.apache.lenya.cms.publication.Document)
107: */
108: public void remove(Document document) throws DocumentException {
109: if (!documents().contains(document)) {
110: throw new DocumentException("Collection [" + this
111: + "] does not contain document [" + document + "]");
112: }
113: documents().remove(document);
114: }
115:
116: private boolean isLoaded = false;
117:
118: /**
119: * Loads the collection from its XML source.
120: */
121: protected final void load() {
122: if (!this .isLoaded) {
123: getLogger().debug("Loading: ");
124: NamespaceHelper helper;
125: try {
126: helper = getNamespaceHelper();
127: } catch (Exception e) {
128: throw new RuntimeException(e);
129: }
130: loadXml(helper);
131: this .isLoaded = true;
132: }
133: }
134:
135: protected void loadXml(NamespaceHelper helper) {
136: try {
137: this .documentsList = new ArrayList();
138:
139: Element collectionElement = helper.getDocument()
140: .getDocumentElement();
141: Element[] documentElements = helper.getChildren(
142: collectionElement, ELEMENT_DOCUMENT);
143:
144: for (int i = 0; i < documentElements.length; i++) {
145: Element documentElement = documentElements[i];
146: Document document = loadDocument(documentElement);
147: this .documentsList.add(document);
148: }
149:
150: if (collectionElement.hasAttribute(ATTRIBUTE_TYPE)) {
151: this .type = collectionElement
152: .getAttribute(ATTRIBUTE_TYPE);
153: }
154:
155: if (collectionElement.hasAttribute(ATTRIBUTE_HREF)) {
156: this .href = collectionElement
157: .getAttribute(ATTRIBUTE_HREF);
158: }
159: } catch (RuntimeException e) {
160: throw e;
161: } catch (Exception e) {
162: throw new RuntimeException(e);
163: }
164: }
165:
166: /**
167: * Loads a document from an XML element.
168: * @param documentElement The XML element.
169: * @return A document.
170: * @throws DocumentBuildException when something went wrong.
171: */
172: protected Document loadDocument(Element documentElement)
173: throws DocumentBuildException {
174: String documentId = documentElement
175: .getAttribute(ATTRIBUTE_UUID);
176: Document document = getDelegate().getFactory().get(
177: getDelegate().getPublication(),
178: getDelegate().getArea(), documentId,
179: getDelegate().getLanguage());
180: return document;
181: }
182:
183: /**
184: * Saves the collection.
185: */
186: public final void save() {
187: try {
188: NamespaceHelper helper = getNamespaceHelper();
189: saveXml(helper);
190: DocumentHelper.writeDocument(helper.getDocument(),
191: getDelegate().getOutputStream());
192: } catch (Exception e) {
193: throw new RuntimeException(e);
194: }
195: }
196:
197: /**
198: * @param helper Save the XML to the provided namespace helper.
199: * @throws TransformerException if an error occurs.
200: * @throws DocumentException if an error occurs.
201: */
202: protected void saveXml(NamespaceHelper helper)
203: throws TransformerException, DocumentException {
204: Element collectionElement = helper.getDocument()
205: .getDocumentElement();
206: if (collectionElement.getAttributeNS(null, ATTRIBUTE_UUID)
207: .equals("")
208: || collectionElement.getAttribute(ATTRIBUTE_UUID)
209: .equals("")) {
210: collectionElement.setAttributeNS(null, ATTRIBUTE_UUID,
211: getDelegate().getUUID());
212: }
213: Element[] existingDocumentElements = helper.getChildren(
214: collectionElement, ELEMENT_DOCUMENT);
215: for (int i = 0; i < existingDocumentElements.length; i++) {
216: collectionElement.removeChild(existingDocumentElements[i]);
217: }
218:
219: collectionElement.setAttribute(ATTRIBUTE_TYPE, getType());
220:
221: collectionElement.setAttribute(ATTRIBUTE_HREF, getHref());
222:
223: collectionElement.normalize();
224:
225: NodeList emptyTextNodes = XPathAPI.selectNodeList(
226: collectionElement, "text()");
227: for (int i = 0; i < emptyTextNodes.getLength(); i++) {
228: Node node = emptyTextNodes.item(i);
229: node = collectionElement.removeChild(node);
230: }
231:
232: Document[] documents = getDocuments();
233: for (int i = 0; i < documents.length; i++) {
234: Element documentElement = createDocumentElement(
235: documents[i], helper);
236: collectionElement.appendChild(documentElement);
237: }
238: }
239:
240: public String getType() {
241: load();
242: return this .type;
243: }
244:
245: /**
246: * Creates an element to store a document.
247: * @param helper The namespace helper of the document.
248: * @param document The document.
249: * @return An XML element.
250: * @throws DocumentException when something went wrong.
251: */
252: protected Element createDocumentElement(Document document,
253: NamespaceHelper helper) throws DocumentException {
254: try {
255: Element documentElement = helper
256: .createElement(ELEMENT_DOCUMENT);
257: documentElement.setAttributeNS(null, ATTRIBUTE_UUID,
258: document.getUUID());
259: return documentElement;
260: } catch (final DOMException e) {
261: throw new DocumentException(e);
262: }
263: }
264:
265: /**
266: * Returns the namespace helper for the XML source.
267: * @return A namespace helper.
268: * @throws DocumentException when something went wrong.
269: * @throws ParserConfigurationException when something went wrong.
270: * @throws SAXException when something went wrong.
271: * @throws IOException when something went wrong.
272: * @throws ServiceException
273: */
274: protected NamespaceHelper getNamespaceHelper()
275: throws DocumentException, ParserConfigurationException,
276: SAXException, IOException, ServiceException {
277:
278: NamespaceHelper helper;
279:
280: if (getDelegate().exists()) {
281: org.w3c.dom.Document document = DocumentHelper
282: .readDocument(getDelegate().getInputStream());
283: helper = new NamespaceHelper(Collection.NAMESPACE,
284: Collection.DEFAULT_PREFIX, document);
285: } else {
286: helper = initializeNamespaceHelper();
287: }
288: return helper;
289: }
290:
291: /**
292: * @return A new, empty namespace helper.
293: */
294: protected NamespaceHelper initializeNamespaceHelper() {
295: NamespaceHelper helper;
296: try {
297: helper = new NamespaceHelper(Collection.NAMESPACE,
298: Collection.DEFAULT_PREFIX, ELEMENT_COLLECTION);
299: } catch (ParserConfigurationException e) {
300: throw new RuntimeException(e);
301: }
302: return helper;
303: }
304:
305: /**
306: * @see org.apache.lenya.modules.collection.Collection#contains(org.apache.lenya.cms.publication.Document)
307: */
308: public boolean contains(Document document) throws DocumentException {
309: return documents().contains(document);
310: }
311:
312: /**
313: * @see org.apache.lenya.modules.collection.Collection#clear()
314: */
315: public void clear() throws DocumentException {
316: documents().clear();
317: }
318:
319: /**
320: * @see org.apache.lenya.modules.collection.Collection#getFirstPosition(org.apache.lenya.cms.publication.Document)
321: */
322: public int getFirstPosition(Document document)
323: throws DocumentException {
324: load();
325: if (!contains(document)) {
326: throw new DocumentException("The collection [" + this
327: + "] does not contain the document [" + document
328: + "]");
329: }
330: return documents().indexOf(document);
331: }
332:
333: /**
334: * @see org.apache.lenya.modules.collection.Collection#size()
335: */
336: public int size() throws DocumentException {
337: return documents().size();
338: }
339:
340: private String type = TYPE_MANUAL;
341:
342: public void setType(String type) {
343: load();
344: if (!Arrays.asList(TYPES).contains(type)) {
345: throw new IllegalArgumentException("The type [" + type
346: + "] is not supported!");
347: }
348: this .type = type;
349: }
350:
351: private String href = "";
352:
353: public String getHref() {
354: load();
355: return this .href;
356: }
357:
358: public void setHref(String href) {
359: load();
360: this.href = href;
361: }
362:
363: }
|