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}