001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.kew.routeheader;
017    
018    import java.io.BufferedReader;
019    import java.io.IOException;
020    import java.io.ObjectInputStream;
021    import java.io.Serializable;
022    import java.io.StringReader;
023    
024    import javax.xml.parsers.DocumentBuilder;
025    import javax.xml.parsers.DocumentBuilderFactory;
026    import javax.xml.parsers.ParserConfigurationException;
027    
028    import org.kuali.rice.core.api.exception.RiceRuntimeException;
029    import org.kuali.rice.kew.api.document.InvalidDocumentContentException;
030    import org.kuali.rice.kew.engine.RouteContext;
031    import org.kuali.rice.kew.api.KewApiConstants;
032    import org.w3c.dom.Document;
033    import org.w3c.dom.Element;
034    import org.w3c.dom.Node;
035    import org.w3c.dom.NodeList;
036    import org.xml.sax.InputSource;
037    import org.xml.sax.SAXException;
038    
039    
040    /**
041     * Standard implementation of {@link DocumentContent} which nows hows to parse a
042     * String that it's constructed with into content with the application,
043     * attribute, and searchable content sections.
044     *
045     * @author Kuali Rice Team (rice.collab@kuali.org)
046     */
047    public class StandardDocumentContent implements DocumentContent, Serializable {
048    
049            private static final long serialVersionUID = -3189330007364191220L;
050            
051            private static final String LEGACY_FLEXDOC_ELEMENT = "flexdoc";
052    
053            private String docContent;
054    
055            private transient Document document;
056    
057            private transient Element applicationContent;
058    
059            private transient Element attributeContent;
060    
061            private transient Element searchableContent;
062    
063            private RouteContext routeContext;
064    
065            public StandardDocumentContent(String docContent) {
066                    this(docContent, null);
067            }
068    
069            public StandardDocumentContent(String docContent, RouteContext routeContext) {
070                    this.routeContext = routeContext;
071                    initialize(docContent, routeContext);
072            }
073    
074            private void initialize(String docContent, RouteContext routeContext) {
075                    if (org.apache.commons.lang.StringUtils.isEmpty(docContent)) {
076                            this.docContent = "";
077                            this.document = null;
078                    } else {
079                            try {
080                                    this.docContent = docContent;
081                                    this.document = parseDocContent(docContent);
082                                    extractElements(this.document);
083                            } catch (IOException e) {
084                                    throw new InvalidDocumentContentException("I/O Error when attempting to parse document content.", e);
085                            } catch (SAXException e) {
086                                    throw new InvalidDocumentContentException("XML parse error when attempting to parse document content.", e);
087                            } catch (ParserConfigurationException e) {
088                                    throw new RiceRuntimeException("XML parser configuration error when attempting to parse document content.", e);
089                            }
090                    }
091            }
092    
093            private Document parseDocContent(String docContent) throws IOException, SAXException, ParserConfigurationException {
094                    DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
095                    return documentBuilder.parse(new InputSource(new BufferedReader(new StringReader(docContent))));
096            }
097    
098            private void extractElements(Document document) {
099                    // this handles backward compatibility in document content
100                    if (!document.getDocumentElement().getNodeName().equals(KewApiConstants.DOCUMENT_CONTENT_ELEMENT)) {
101                            // if the root element is the flexdoc element (pre Workflow 2.0)
102                            // then designate that as attribute content
103                            if (document.getDocumentElement().getNodeName().equals(LEGACY_FLEXDOC_ELEMENT)) {
104                                    attributeContent = document.getDocumentElement();
105                            } else {
106                                    applicationContent = document.getDocumentElement();
107                            }
108                    } else {
109                            NodeList nodes = document.getDocumentElement().getChildNodes();
110                            for (int index = 0; index < nodes.getLength(); index++) {
111                                    Node node = nodes.item(index);
112                                    if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(KewApiConstants.APPLICATION_CONTENT_ELEMENT)) {
113                                            int numChildElements = 0;
114                                            for (int childIndex = 0; childIndex < node.getChildNodes().getLength(); childIndex++) {
115                                                    Node child = (Node) node.getChildNodes().item(childIndex);
116                                                    if (child.getNodeType() == Node.ELEMENT_NODE) {
117                                                            applicationContent = (Element) child;
118                                                            numChildElements++;
119                                                    }
120                                            }
121                                            // TODO can we have application content without a root node?
122                                            if (numChildElements > 1) {
123                                                    applicationContent = (Element) node;
124                                            }
125                                    } else if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(KewApiConstants.ATTRIBUTE_CONTENT_ELEMENT)) {
126                                            attributeContent = (Element) node;
127                                    } else if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(KewApiConstants.SEARCHABLE_CONTENT_ELEMENT)) {
128                                            searchableContent = (Element) node;
129                                    }
130                            }
131                    }
132            }
133    
134            public Element getApplicationContent() {
135                    return applicationContent;
136            }
137    
138            public Element getAttributeContent() {
139                    return attributeContent;
140            }
141    
142            public String getDocContent() {
143                    return docContent;
144            }
145    
146            public Document getDocument() {
147                    return document;
148            }
149    
150            public Element getSearchableContent() {
151                    return searchableContent;
152            }
153    
154            public RouteContext getRouteContext() {
155                    return this.routeContext;
156            }
157    
158            private void readObject(ObjectInputStream ais) throws IOException, ClassNotFoundException {
159                    ais.defaultReadObject();
160                    initialize(this.docContent, this.routeContext);
161            }
162    
163    }