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    }