File: AbstractXmlResourceFactory.java

package info (click to toggle)
libxml-java 1.1.7-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 544 kB
  • sloc: java: 4,760; xml: 1,011; makefile: 10
file content (424 lines) | stat: -rw-r--r-- 15,423 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * Copyright (c) 2006 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.  All rights reserved.
 */

package org.pentaho.reporting.libraries.xmlns.parser;

import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.resourceloader.CompoundResource;
import org.pentaho.reporting.libraries.resourceloader.FactoryParameterKey;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceFactory;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.resourceloader.loader.raw.RawResourceData;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.config.DefaultConfiguration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * A base-class for resource-factories that load their resources from XML files. This class provides a multiplexing
 * option. For this, the parser looks at the root-element of the document to be parsed and selects the most suitable
 * XmlFactoryModule implementation registered.
 *
 * @author Thomas Morgner
 */
public abstract class AbstractXmlResourceFactory implements ResourceFactory
{
  private static final Log logger = LogFactory.getLog(AbstractXmlResourceFactory.class);

  /**
   * A key for the content base.
   */
  public static final String CONTENTBASE_KEY = "content-base";
  private static final byte[] EMPTY_DATA = new byte[0];

  private ArrayList modules;
  private SAXParserFactory factory;

  /**
   * Default-Constructor.
   */
  protected AbstractXmlResourceFactory()
  {
    modules = new ArrayList();
  }


  /**
   * Returns a SAX parser.
   *
   * @return a SAXParser.
   * @throws ParserConfigurationException if there is a problem configuring the
   *                                      parser.
   * @throws SAXException                 if there is a problem with the parser
   *                                      initialisation
   */
  protected SAXParser getParser()
      throws ParserConfigurationException, SAXException
  {
    if (this.factory == null)
    {
      this.factory = SAXParserFactory.newInstance();
    }
    return this.factory.newSAXParser();
  }


  /**
   * Configures the xml reader. Use this to set features or properties before
   * the documents get parsed.
   *
   * @param handler the parser implementation that will handle the
   *                SAX-Callbacks.
   * @param reader  the xml reader that should be configured.
   */
  protected void configureReader(final XMLReader reader,
                                 final MultiplexRootElementHandler handler)
  {
    try
    {
      reader.setProperty
          ("http://xml.org/sax/properties/lexical-handler",
              handler.getCommentHandler());
    }
    catch (SAXException se)
    {
      logger.debug("Comments are not supported by this SAX implementation.");
    }

    try
    {
      reader.setFeature("http://xml.org/sax/features/xmlns-uris", true);
    }
    catch (SAXException e)
    {
      handler.setXmlnsUrisNotAvailable(true);
    }
    try
    {
      reader.setFeature("http://xml.org/sax/features/namespaces", true);
      reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
    }
    catch (SAXException e)
    {
      logger.warn("No Namespace features will be available. (Yes, this is serious)");
    }
  }

  /**
   * Creates a resource by interpreting the data given in the resource-data object. If additional datastreams need to
   * be parsed, the provided resource manager should be used. This method parses the given resource-data as XML stream.
   *
   * @param manager the resource manager used for all resource loading.
   * @param data    the resource-data from where the binary data is read.
   * @param context the resource context used to resolve relative resource paths.
   * @return the parsed result, never null.
   * @throws ResourceCreationException if the resource could not be parsed due to syntaxctial or logical errors in the data.
   * @throws ResourceLoadingException  if the resource could not be accessed from the physical storage.
   */
  public Resource create(final ResourceManager manager,
                         final ResourceData data,
                         final ResourceKey context)
      throws ResourceCreationException, ResourceLoadingException
  {
    try
    {
      final SAXParser parser = getParser();

      final XMLReader reader = parser.getXMLReader();
      final XmlFactoryModule[] rootHandlers = getModules();
      if (rootHandlers.length == 0)
      {
        throw new ResourceCreationException("There are no root-handlers registered for the factory for type " + getFactoryType());
      }

      final ResourceDataInputSource input = new ResourceDataInputSource(data, manager);

      final ResourceKey contextKey;
      final long version;
      final ResourceKey targetKey = data.getKey();
      if (context == null)
      {
        contextKey = targetKey;
        version = data.getVersion(manager);
      }
      else
      {
        contextKey = context;
        version = -1;
      }

      final MultiplexRootElementHandler handler =
          new MultiplexRootElementHandler(manager, targetKey,
              contextKey, version, rootHandlers);

      final DefaultConfiguration parserConfiguration = handler.getParserConfiguration();
      final URL value = manager.toURL(contextKey);
      if (value != null)
      {
        parserConfiguration.setConfigProperty(CONTENTBASE_KEY, value.toExternalForm());
      }

      configureReader(reader, handler);
      reader.setContentHandler(handler);
      reader.setDTDHandler(handler);
      reader.setEntityResolver(handler.getEntityResolver());
      reader.setErrorHandler(getErrorHandler());

      final Map parameters = targetKey.getFactoryParameters();
      final Iterator it = parameters.keySet().iterator();
      while (it.hasNext())
      {
        final Object o = it.next();
        if (o instanceof FactoryParameterKey)
        {
          final FactoryParameterKey fpk = (FactoryParameterKey) o;
          handler.setHelperObject(fpk.getName(), parameters.get(fpk));
        }
      }

      reader.parse(input);

      final Object createdProduct = finishResult
          (handler.getResult(), manager, data, contextKey);
      handler.getDependencyCollector().add(targetKey, data.getVersion(manager));
      return createResource(targetKey, handler, createdProduct, getFactoryType());
    }
    catch (ParserConfigurationException e)
    {
      throw new ResourceCreationException("Unable to initialize the XML-Parser", e);
    }
    catch (SAXException e)
    {
      throw new ResourceCreationException("Unable to parse the document: " + data.getKey(), e);
    }
    catch (IOException e)
    {
      throw new ResourceLoadingException("Unable to read the stream from document: " + data.getKey(), e);
    }
  }

  /**
   * A method to allow to invoke the parsing without accessing the LibLoader layer. The data to be parsed is held in
   * the given InputSource object.
   *
   * @param manager the resource manager used for all resource loading.
   * @param input    the raw-data given as SAX-InputSource.
   * @param context the resource context used to resolve relative resource paths.
   * @param parameters the parse parameters.
   * @return the parsed result, never null.
   * @throws ResourceCreationException if the resource could not be parsed due to syntaxctial or logical errors in the data.
   * @throws ResourceLoadingException  if the resource could not be accessed from the physical storage.
   * @throws ResourceKeyCreationException if creating the context key failed.
   */
  public Object parseDirectly(final ResourceManager manager,
                              final InputSource input,
                              final ResourceKey context,
                              final Map parameters)
      throws ResourceKeyCreationException, ResourceCreationException, ResourceLoadingException
  {
    try
    {
      final SAXParser parser = getParser();

      final XMLReader reader = parser.getXMLReader();
      final XmlFactoryModule[] rootHandlers = getModules();

      final ResourceKey targetKey = manager.createKey(EMPTY_DATA);
      final ResourceKey contextKey;
      if (context == null)
      {
        contextKey = targetKey;
      }
      else
      {
        contextKey = context;
      }

      final MultiplexRootElementHandler handler =
          new MultiplexRootElementHandler(manager, targetKey, contextKey, -1, rootHandlers);

      final DefaultConfiguration parserConfiguration = handler.getParserConfiguration();
      final URL value = manager.toURL(contextKey);
      if (value != null)
      {
        parserConfiguration.setConfigProperty(CONTENTBASE_KEY, value.toExternalForm());
      }

      configureReader(reader, handler);
      reader.setContentHandler(handler);
      reader.setDTDHandler(handler);
      reader.setEntityResolver(handler.getEntityResolver());
      reader.setErrorHandler(getErrorHandler());

      final Iterator it = parameters.keySet().iterator();
      while (it.hasNext())
      {
        final Object o = it.next();
        if (o instanceof FactoryParameterKey)
        {
          final FactoryParameterKey fpk = (FactoryParameterKey) o;
          handler.setHelperObject(fpk.getName(), parameters.get(fpk));
        }
      }

      reader.parse(input);

      return finishResult(handler.getResult(), manager, new RawResourceData(targetKey), contextKey);
    }
    catch (ParserConfigurationException e)
    {
      throw new ResourceCreationException
          ("Unable to initialize the XML-Parser", e);
    }
    catch (SAXException e)
    {
      throw new ResourceCreationException("Unable to parse the document", e);
    }
    catch (IOException e)
    {
      throw new ResourceLoadingException("Unable to read the stream", e);
    }

  }

  /**
   * Returns the registered XmlFactoryModules as array.
   *
   * @return the modules as array.
   */
  private XmlFactoryModule[] getModules()
  {
    return (XmlFactoryModule[]) modules.toArray(new XmlFactoryModule[modules.size()]);
  }

  /**
   * Creates a Resource object for the given product. By default this returns a compound-resource that holds
   * all the key that identify the resources used during the content production.
   *
   * @param targetKey the target key.
   * @param handler the root handler used for the parsing.
   * @param createdProduct the created product.
   * @return the product wrapped into a resource object.
   */
  protected Resource createResource(final ResourceKey targetKey,
                                    final MultiplexRootElementHandler handler,
                                    final Object createdProduct,
                                    final Class createdType)
  {
    return new CompoundResource
        (targetKey, handler.getDependencyCollector(), createdProduct, createdType);
  }

  /**
   * Finishes up the result. This can be used for general clean up and post-parse initializaion of the result.
   * The default implementation does nothing and just returns the object itself.
   *
   * @param res the parsed resource.
   * @param manager the resource manager that was used to load the resource.
   * @param data the data object from where the resource is loaded.
   * @param context the context that resolves relative resource paths.
   * @return the parsed resource.
   * @throws ResourceCreationException if the post initialization fails.
   * @throws ResourceLoadingException if loading external resources failed with an IO error.
   */
  protected Object finishResult(final Object res,
                                final ResourceManager manager,
                                final ResourceData data,
                                final ResourceKey context)
      throws ResourceCreationException, ResourceLoadingException
  {
    return res;
  }

  /**
   * Returns the configuration that should be used to initialize this factory.
   *
   * @return the configuration for initializing the factory.
   */
  protected abstract Configuration getConfiguration();

  /**
   * Loads all XmlFactoryModule-implementations from the given configuration.
   *
   * @see #getConfiguration() 
   */
  public void initializeDefaults()
  {
    final String type = getFactoryType().getName();
    final String prefix = ResourceFactory.CONFIG_PREFIX + type;
    final Configuration config = getConfiguration();
    final Iterator itType = config.findPropertyKeys(prefix);
    while (itType.hasNext())
    {
      final String key = (String) itType.next();
      final String modClass = config.getConfigProperty(key);
      final Object maybeFactory = ObjectUtilities.loadAndInstantiate
          (modClass, AbstractXmlResourceFactory.class, XmlFactoryModule.class);
      if (maybeFactory instanceof XmlFactoryModule == false)
      {
        continue;
      }
      registerModule((XmlFactoryModule) maybeFactory);
    }
  }

  /**
   * Registers a factory module for being used during the parsing. If the factory module does not return a
   * result that matches the factory's type, the parsing will always fail.
   *
   * @param factoryModule the factory module.
   * @throws NullPointerException if the module given is null.
   */
  public void registerModule(final XmlFactoryModule factoryModule)
  {
    if (factoryModule == null)
    {
      throw new NullPointerException();
    }
    modules.add(factoryModule);
  }

  /**
   * Returns the XML-Error handler that should be registered with the XML parser. By default, this returns
   * a logger.
   *
   * @return the error handler.
   */
  protected ErrorHandler getErrorHandler()
  {
    return new LoggingErrorHandler();
  }
}