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 }