001 /**
002 * Copyright 2005-2014 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.edl.impl.xml;
017
018 import org.apache.log4j.Logger;
019 import org.kuali.rice.core.api.impex.xml.XmlIngestionException;
020 import org.kuali.rice.coreservice.api.style.Style;
021 import org.kuali.rice.coreservice.api.style.StyleService;
022 import org.kuali.rice.core.api.util.xml.XmlException;
023 import org.kuali.rice.core.api.util.xml.XmlJotter;
024 import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
025 import org.kuali.rice.edl.impl.EDLXmlUtils;
026 import org.kuali.rice.edl.impl.bo.EDocLiteAssociation;
027 import org.kuali.rice.edl.impl.bo.EDocLiteDefinition;
028 import org.kuali.rice.edl.impl.service.EDocLiteService;
029 import org.kuali.rice.edl.impl.service.EdlServiceLocator;
030 import org.kuali.rice.kew.rule.bo.RuleAttribute;
031 import org.kuali.rice.kew.service.KEWServiceLocator;
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
037 import javax.xml.parsers.DocumentBuilder;
038 import javax.xml.xpath.XPath;
039 import javax.xml.xpath.XPathConstants;
040 import javax.xml.xpath.XPathExpressionException;
041 import javax.xml.xpath.XPathFactory;
042 import java.io.InputStream;
043 import java.util.ArrayList;
044 import java.util.Collection;
045 import java.util.Iterator;
046
047
048 /**
049 * An XML parser which parses EDocLite definitions.
050 *
051 * @author Kuali Rice Team (rice.collab@kuali.org)
052 */
053 public class EDocLiteXmlParser {
054
055 private static final Logger LOG = Logger.getLogger(EDocLiteXmlParser.class);
056
057 public static void loadXml(InputStream inputStream, String principalId) {
058 DocumentBuilder db = EDLXmlUtils.getDocumentBuilder();
059 XPath xpath = XPathFactory.newInstance().newXPath();
060 Document doc;
061 // parse and save EDocLiteDefinition, EDocLiteStyle, or EDocLiteAssociation xml from to-be-determined XML format
062 //try {
063 try {
064 doc = db.parse(inputStream);
065 } catch (Exception e) {
066 throw generateException("Error parsing EDocLite XML file", e);
067 }
068 /*try {
069 LOG.info(XmlHelper.writeNode(doc.getFirstChild(), true));
070 } catch (TransformerException e) {
071 LOG.warn("Error displaying document");
072 }*/
073
074 NodeList edls;
075 try {
076 edls = (NodeList) xpath.evaluate("//edoclite", doc.getFirstChild(), XPathConstants.NODESET);
077 } catch (XPathExpressionException e) {
078 throw generateException("Error evaluating XPath expression", e);
079 }
080
081 for (int i = 0; i < edls.getLength(); i++) {
082 Node edl = edls.item(i);
083 NodeList children = edl.getChildNodes();
084 for (int j = 0; j < children.getLength(); j++) {
085 Node node = children.item(j);
086 /*try {
087 LOG.info(XmlHelper.writeNode(node, true));
088 } catch (TransformerException te) {
089 LOG.warn("Error displaying node");
090 }*/
091 if (node.getNodeType() == Node.ELEMENT_NODE) {
092 Element e = (Element) node;
093 if ("style".equals(node.getNodeName())) {
094 LOG.debug("Digesting EDocLiteStyle: " + e.getAttribute("name"));
095 Style style = parseStyle(e);
096 getStyleService().saveStyle(style);
097 } else if ("edl".equals(node.getNodeName())) {
098 LOG.debug("Digesting EDocLiteDefinition: " + e.getAttribute("name"));
099 EDocLiteDefinition def = parseEDocLiteDefinition(e);
100 getEDLService().saveEDocLiteDefinition(def);
101 } else if ("association".equals(node.getNodeName())) {
102 LOG.debug("Digesting EDocLiteAssociation: " + e.getAttribute("name"));
103 EDocLiteAssociation assoc = parseEDocLiteAssociation(e);
104 getEDLService().saveEDocLiteAssociation(assoc);
105 } else {
106 // LOG.debug("Unrecognized element '" + node.getNodeName() + "' in EDocLite XML doc");
107 }
108 }
109 }
110 }
111 //} catch (Exception e) {
112 // throw generateException("Error parsing EDocLite XML file", e);
113 //}
114 }
115
116 private static XmlIngestionException generateException(String error, Throwable cause) {
117 throw new XmlIngestionException(error, cause);
118 }
119
120 /**
121 * Parses an EDocLiteAssocation
122 *
123 * @param e
124 * element to parse
125 * @return an EDocLiteAssocation
126 */
127 private static EDocLiteAssociation parseEDocLiteAssociation(Element e) {
128 String docType = EDLXmlUtils.getChildElementTextValue(e, "docType");
129 if (docType == null) {
130 throw generateMissingChildException("association", "docType");
131 }
132 EDocLiteAssociation assoc = new EDocLiteAssociation();
133 assoc.setEdlName(docType);
134 assoc.setDefinition(EDLXmlUtils.getChildElementTextValue(e, "definition"));
135 assoc.setStyle(EDLXmlUtils.getChildElementTextValue(e, "style"));
136 assoc.setActiveInd(Boolean.valueOf(EDLXmlUtils.getChildElementTextValue(e, "active")));
137 return assoc;
138 }
139
140 /**
141 * Parses an EDocLiteStyle
142 *
143 * @param e
144 * element to parse
145 * @return an EDocLiteStyle
146 */
147 private static Style parseStyle(Element e) {
148 String name = e.getAttribute("name");
149 if (name == null || name.length() == 0) {
150 throw generateMissingAttribException("style", "name");
151 }
152 Style.Builder styleBuilder = Style.Builder.create(name);
153 Element stylesheet = null;
154 NodeList children = e.getChildNodes();
155 for (int i = 0; i < children.getLength(); i++) {
156 Node child = children.item(i);
157 /*
158 * LOG.debug("NodeName: " + child.getNodeName()); LOG.debug("LocalName: " + child.getLocalName()); LOG.debug("Prefix: " + child.getPrefix()); LOG.debug("NS URI: " + child.getNamespaceURI());
159 */
160 if (child.getNodeType() == Node.ELEMENT_NODE && "xsl:stylesheet".equals(child.getNodeName())) {
161 stylesheet = (Element) child;
162 break;
163 }
164 }
165 if (stylesheet == null) {
166 throw generateMissingChildException("style", "xsl:stylesheet");
167 }
168 try {
169 styleBuilder.setXmlContent(XmlJotter.jotNode(stylesheet, true));
170 } catch (XmlException te) {
171 throw generateSerializationException("style", te);
172 }
173 return styleBuilder.build();
174 }
175
176 /**
177 * Parses an EDocLiteDefinition
178 *
179 * @param e
180 * element to parse
181 * @return an EDocLiteDefinition
182 */
183 private static EDocLiteDefinition parseEDocLiteDefinition(Element e) {
184 EDocLiteDefinition def = new EDocLiteDefinition();
185 String name = e.getAttribute("name");
186 if (name == null || name.length() == 0) {
187 throw generateMissingAttribException(EDLXmlUtils.EDL_E, "name");
188 }
189 def.setName(name);
190
191 // do some validation to ensure that any attributes referenced actually exist
192 // blow up if there is a problem
193
194 XPath xpath = XPathFactory.newInstance().newXPath();
195 NodeList fields;
196 try {
197 fields = (NodeList) xpath.evaluate("fieldDef", e, XPathConstants.NODESET);
198 } catch (XPathExpressionException xpee) {
199 throw new XmlIngestionException("Invalid EDocLiteDefinition", xpee);
200 }
201
202 if (fields != null) {
203 Collection invalidAttributes = new ArrayList(5);
204 for (int i = 0; i < fields.getLength(); i++) {
205 Node node = (Node) fields.item(i);
206 // they should all be Element...
207 if (node instanceof Element) {
208 Element field = (Element) node;
209 // rely on XML validation to ensure this is present
210 String fieldName = field.getAttribute("name");
211 String attribute = field.getAttribute("attributeName");
212 if (attribute != null && attribute.length() > 0) {
213 RuleAttribute ruleAttrib = KEWServiceLocator.getRuleAttributeService().findByName(attribute);
214 if (ruleAttrib == null) {
215 LOG.error("Invalid attribute referenced in EDocLite definition: " + attribute);
216 invalidAttributes.add("Attribute '" + attribute + "' referenced in field '" + fieldName + "' not found");
217 }
218 }
219 }
220 }
221 if (invalidAttributes.size() > 0) {
222 LOG.error("Invalid attributes referenced in EDocLite definition");
223 StringBuffer message = new StringBuffer("EDocLite definition contains references to non-existent attributes;\n");
224 Iterator it = invalidAttributes.iterator();
225 while (it.hasNext()) {
226 message.append(it.next());
227 message.append("\n");
228 }
229 throw new XmlIngestionException(message.toString());
230 }
231 }
232
233 try {
234 def.setXmlContent(XmlJotter.jotNode(e, true));
235 } catch (XmlException te) {
236 throw generateSerializationException(EDLXmlUtils.EDL_E, te);
237 }
238 return def;
239 }
240
241 private static XmlIngestionException generateMissingAttribException(String element, String attrib) {
242 return generateException("EDocLite '" + element + "' element must contain a '" + attrib + "' attribute", null);
243 }
244
245 private static XmlIngestionException generateMissingChildException(String element, String child) {
246 return generateException("EDocLite '" + element + "' element must contain a '" + child + "' child element", null);
247 }
248
249 private static XmlIngestionException generateSerializationException(String element, XmlException cause) {
250 return generateException("Error serializing EDocLite '" + element + "' element", cause);
251 }
252
253 private static EDocLiteService getEDLService() {
254 return EdlServiceLocator.getEDocLiteService();
255 }
256
257 private static StyleService getStyleService() {
258 return CoreServiceApiServiceLocator.getStyleService();
259 }
260 }