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