001    package org.apache.fulcrum.intake.transform;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.BufferedReader;
023    import java.io.FileReader;
024    import java.io.IOException;
025    
026    import javax.xml.parsers.ParserConfigurationException;
027    import javax.xml.parsers.SAXParser;
028    import javax.xml.parsers.SAXParserFactory;
029    
030    import org.apache.avalon.framework.logger.LogEnabled;
031    import org.apache.avalon.framework.logger.Logger;
032    import org.apache.fulcrum.intake.xmlmodel.AppData;
033    import org.apache.fulcrum.intake.xmlmodel.Rule;
034    import org.apache.fulcrum.intake.xmlmodel.XmlField;
035    import org.apache.fulcrum.intake.xmlmodel.XmlGroup;
036    import org.xml.sax.Attributes;
037    import org.xml.sax.InputSource;
038    import org.xml.sax.SAXException;
039    import org.xml.sax.SAXParseException;
040    import org.xml.sax.helpers.DefaultHandler;
041    
042    /**
043     * A Class that is used to parse an input
044     * xml schema file and creates and AppData java structure.
045     * It uses apache Xerces to do the xml parsing.
046     *
047     * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
048     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
049     * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
050     * @version $Id: XmlToAppData.java 732101 2009-01-06 20:30:43Z tv $
051     */
052    public class XmlToAppData extends DefaultHandler
053        implements LogEnabled
054    {
055        /** Logging */
056        private Logger log;
057    
058        private AppData app;
059        private XmlGroup currGroup;
060        private XmlField currField;
061        private Rule currRule;
062        private String currElement;
063        private StringBuffer chars;
064    
065        private static SAXParserFactory saxFactory;
066    
067        static
068        {
069            saxFactory = SAXParserFactory.newInstance();
070            saxFactory.setValidating(true);
071        }
072    
073        /**
074         * Creates a new instance of the Intake XML Parser
075         */
076        public XmlToAppData()
077        {
078            app = new AppData();
079        }
080    
081        /**
082         * Parses a XML input file and returns a newly created and
083         * populated AppData structure.
084         *
085         * @param xmlFile The input file to parse.
086         * @return AppData populated by <code>xmlFile</code>.
087         * @throws ParserConfigurationException
088         * @throws SAXException
089         * @throws IOException
090         */
091        public AppData parseFile(String xmlFile)
092                throws ParserConfigurationException, SAXException, IOException
093        {
094            SAXParser parser = saxFactory.newSAXParser();
095    
096            FileReader fr = new FileReader(xmlFile);
097            BufferedReader br = new BufferedReader(fr);
098            
099            chars = new StringBuffer();
100            
101            try
102            {
103                InputSource is = new InputSource(br);
104                parser.parse(is, this);
105            }
106            finally
107            {
108                br.close();
109            }
110    
111            return app;
112        }
113    
114        /**
115         * Provide an Avalon logger
116         * 
117         * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger)
118         */
119        public void enableLogging(Logger logger)
120        {
121            this.log = logger.getChildLogger("XmlToAppData");
122            
123        }
124    
125        /**
126         * EntityResolver implementation. Called by the XML parser
127         *
128         * @return an InputSource for the database.dtd file
129         */
130        public InputSource resolveEntity(String publicId, String systemId)
131        {
132            return new DTDResolver().resolveEntity(publicId, systemId);
133        }
134    
135        /**
136         * Handles opening elements of the xml file.
137         */
138        public void startElement(String uri, String localName,
139                                 String rawName, Attributes attributes)
140        {
141            currElement = rawName;
142            if (rawName.equals("input-data"))
143            {
144                app.loadFromXML(attributes);
145            }
146            else if (rawName.equals("group"))
147            {
148                currGroup = app.addGroup(attributes);
149            }
150            else if (rawName.equals("field"))
151            {
152                currField = currGroup.addField(attributes);
153            }
154            else if (rawName.equals("rule"))
155            {
156                currRule = currField.addRule(attributes);
157            }
158        }
159    
160        /**
161         * Handles closing elements of the xml file.
162         * 
163         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
164         */
165        public void endElement(String uri, String localName, String name) throws SAXException
166        {
167            if ("rule".equals(currElement) && chars.length() > 0)
168            {
169                currRule.setMessage(chars.toString());
170            }
171            else if ("required-message".equals(currElement) && chars.length() > 0)
172            {
173                log.warn("The required-message element is deprecated!  " +
174                        "You should update your intake.xml file to use the " +
175                        "'required' rule instead.");
176                currField.setIfRequiredMessage(chars.toString());
177            }
178            
179            chars = new StringBuffer();
180        }
181    
182        /**
183         * Handles the character data, which we are using to specify the
184         * error message.
185         */
186        public void characters(char[] mesgArray, int start, int length)
187        {
188            this.chars.append(mesgArray, start, length);
189        }
190    
191        /**
192         * Callback function for the xml parser to give warnings.
193         *
194         * @param spe a <code>SAXParseException</code> value
195         */
196        public void warning(SAXParseException spe)
197        {
198            log.warn("Parser Exception: " +
199                    "Line " + spe.getLineNumber() +
200                    " Row: " + spe.getColumnNumber() +
201                    " Msg: " + spe.getMessage());
202        }
203    
204        /**
205         * Callback function for the xml parser to give errors.
206         *
207         * @param spe a <code>SAXParseException</code> value
208         */
209        public void error(SAXParseException spe)
210        {
211            log.error("Parser Exception: " +
212                    "Line " + spe.getLineNumber() +
213                    " Row: " + spe.getColumnNumber() +
214                    " Msg: " + spe.getMessage());
215        }
216    
217        /**
218         * Callback function for the xml parser to give fatalErrors.
219         *
220         * @param spe a <code>SAXParseException</code> value
221         */
222        public void fatalError(SAXParseException spe)
223        {
224            log.fatalError("Parser Exception: " +
225                    "Line " + spe.getLineNumber() +
226                    " Row: " + spe.getColumnNumber() +
227                    " Msg: " + spe.getMessage());
228        }
229    }