View Javadoc

1   /*
2    * Copyright 2010-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.xml;
17  
18  import java.io.StringReader;
19  import java.util.Iterator;
20  
21  import javax.xml.stream.XMLEventReader;
22  import javax.xml.stream.XMLInputFactory;
23  import javax.xml.stream.XMLStreamException;
24  import javax.xml.stream.XMLStreamWriter;
25  import javax.xml.stream.events.Attribute;
26  import javax.xml.stream.events.Characters;
27  import javax.xml.stream.events.StartElement;
28  import javax.xml.stream.events.XMLEvent;
29  
30  /**
31   * Copies XML information read from one source to another.
32   *
33   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
34   * @version $Revision:591 $
35   */
36  public class CopyReader
37  {
38    // ********************************* Fields *********************************
39  
40    // --- constants ------------------------------------------------------------
41  
42    /**
43     * The generic identifier (GI) of the artificial element that is added if the
44     * fragment is not enclosed in an element.
45     * <p>
46     * The value of this constant is {@value}.
47     */
48    private static final String ARTIFICIAL_GI = "root";
49  
50    /**
51     * The artificial start tag with the {@link #ARTIFICIAL_GI}.
52     * <p>
53     * The value of this constant is {@value}.
54     */
55    private static final String ARTIFICIAL_START_TAG = '<' + ARTIFICIAL_GI + '>';
56  
57    /**
58     * The artificial end tag with the {@link #ARTIFICIAL_GI}.
59     * <p>
60     * The value of this constant is {@value}.
61     */
62    private static final String ARTIFICIAL_END_TAG = "</" + ARTIFICIAL_GI + '>';
63  
64    // --- members --------------------------------------------------------------
65  
66    /**
67     * The writer to write to. The caller is responsible to release the resource.
68     */
69    private final XMLStreamWriter xmlWriter;
70  
71    // ****************************** Initializer *******************************
72  
73    // ****************************** Constructors ******************************
74  
75    /**
76     * Default constructor.
77     *
78     * @param xmlWriter the writer to write to.
79     */
80    public CopyReader(final XMLStreamWriter xmlWriter)
81    {
82      this.xmlWriter = xmlWriter;
83    }
84  
85    // ****************************** Inner Classes *****************************
86  
87    // ********************************* Methods ********************************
88  
89    // --- init -----------------------------------------------------------------
90  
91    // --- get&set --------------------------------------------------------------
92  
93    // --- business -------------------------------------------------------------
94  
95    /**
96     * Copies the XML fragment to the writer.
97     *
98     * @param xmlFragment the XML fragment to read and write to the writer.
99     * @throws XMLStreamException on any problem reading from the fragment.
100    */
101   public void copy(final String xmlFragment) throws XMLStreamException
102   {
103     final XMLInputFactory factory = XMLInputFactory.newInstance();
104     final boolean isFragment = isFragment(xmlFragment);
105     final XMLEventReader eventReader;
106     if (isFragment)
107     {
108       eventReader =
109           factory.createXMLEventReader(new StringReader(ARTIFICIAL_START_TAG
110                                                         + xmlFragment
111                                                         + ARTIFICIAL_END_TAG));
112       boolean run = true;
113       while (run && eventReader.hasNext())
114       {
115         final XMLEvent event = eventReader.nextEvent();
116         if (event.getEventType() == XMLEvent.START_ELEMENT)
117         {
118           run = false;
119         }
120       }
121     }
122     else
123     {
124       eventReader = factory.createXMLEventReader(new StringReader(xmlFragment));
125     }
126     parse(eventReader);
127   }
128 
129   /**
130    * Checks if the given XML fragment is not enclosed within an element.
131    *
132    * @param xmlFragment the XML fragment to check.
133    * @return <code>true</code> if the fragment is not enclosed in an element,
134    *         <code>false</code> otherwise.
135    */
136   private static boolean isFragment(final String xmlFragment)
137   {
138     return xmlFragment.charAt(0) != '<';
139   }
140 
141   /**
142    * Parses the events from the given event reader.
143    *
144    * @param eventReader the event reader to read the events from.
145    * @throws XMLStreamException on any problem reading the events.
146    */
147   private void parse(final XMLEventReader eventReader)
148     throws XMLStreamException
149   {
150     int level = 0; // NOPMD
151     while (eventReader.hasNext())
152     {
153       final XMLEvent event = eventReader.nextEvent();
154       switch (event.getEventType())
155       {
156         case XMLEvent.START_ELEMENT:
157           level++; // NOPMD
158           handleStartElement(event);
159           break;
160         case XMLEvent.END_ELEMENT:
161           level--;
162           if (level >= 0)
163           {
164             xmlWriter.writeEndElement();
165           }
166           break;
167         case XMLEvent.CHARACTERS:
168           final Characters chars = event.asCharacters();
169           xmlWriter.writeCharacters(chars.getData());
170         default:
171           // Just skip...
172           break;
173       }
174     }
175   }
176 
177   /**
178    * Handles the start element with all of its attributes.
179    *
180    * @param event the event required to be a {@link StartElement}.
181    * @throws XMLStreamException on any parsing problem.
182    */
183   @SuppressWarnings("unchecked")
184   private void handleStartElement(final XMLEvent event)
185     throws XMLStreamException
186   {
187     final StartElement element = event.asStartElement();
188     final String gi = element.getName().toString();
189     xmlWriter.writeStartElement(gi);
190     for (final Iterator<Attribute> i = element.getAttributes(); i.hasNext();)
191     {
192       final Attribute attribute = i.next();
193       xmlWriter.writeAttribute(attribute.getName().toString(),
194           attribute.getValue());
195     }
196   }
197 
198   // --- object basics --------------------------------------------------------
199 
200 }