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