EMMA Coverage Report (generated Fri Jun 07 22:14:55 CEST 2013)
[all classes][org.jdtaus.banking.ri.txtdirectory]

COVERAGE SUMMARY FOR SOURCE FILE [JaxpTextschluesselVerzeichnis.java]

nameclass, %method, %block, %line, %
JaxpTextschluesselVerzeichnis.java100% (2/2)73%  (30/41)69%  (1175/1712)75%  (233.2/310)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JaxpTextschluesselVerzeichnis$1100% (1/1)25%  (1/4)10%  (9/87)20%  (1/5)
error (SAXParseException): void 0%   (0/1)0%   (0/26)0%   (0/1)
fatalError (SAXParseException): void 0%   (0/1)0%   (0/26)0%   (0/1)
warning (SAXParseException): void 0%   (0/1)0%   (0/26)0%   (0/2)
JaxpTextschluesselVerzeichnis$1 (JaxpTextschluesselVerzeichnis, URL): void 100% (1/1)100% (9/9)100% (1/1)
     
class JaxpTextschluesselVerzeichnis100% (1/1)78%  (29/37)72%  (1166/1625)76%  (232.2/305)
JaxpTextschluesselVerzeichnis (long, long): void 0%   (0/1)0%   (0/23)0%   (0/6)
getChangeInfoMessage (Locale, String): String 0%   (0/1)0%   (0/12)0%   (0/1)
getDuplicateTextschluesselMessage (Locale, Number, Number): String 0%   (0/1)0%   (0/16)0%   (0/1)
getNoJAXPValidationWarningMessage (Locale, String): String 0%   (0/1)0%   (0/12)0%   (0/1)
getNotMonitoringWarningMessage (Locale, String, String): String 0%   (0/1)0%   (0/16)0%   (0/1)
getParseExceptionMessage (Locale, String, String, Number, Number): String 0%   (0/1)0%   (0/24)0%   (0/1)
getUnsupportedModelVersionMessage (Locale, String): String 0%   (0/1)0%   (0/12)0%   (0/1)
getUnsupportedNamespaceMessage (Locale, String): String 0%   (0/1)0%   (0/12)0%   (0/1)
getTextschluessel (int, int): Textschluessel 100% (1/1)34%  (22/65)40%  (4/10)
assertValidProperties (): void 100% (1/1)41%  (11/27)60%  (3/5)
search (boolean, boolean, Date): Textschluessel [] 100% (1/1)47%  (7/15)67%  (2/3)
monitorResource (URL): void 100% (1/1)52%  (32/62)52%  (5.7/11)
assertInitialized (): void 100% (1/1)58%  (134/232)52%  (26/50)
getTextschluessel (int, int, Date): Textschluessel 100% (1/1)60%  (29/48)60%  (6/10)
getDocumentBuilder (): DocumentBuilder 100% (1/1)64%  (23/36)73%  (8/11)
transformDocument (Document): List 100% (1/1)75%  (79/106)86%  (19/22)
searchTextschluessel (Boolean, Boolean, Date): Textschluessel [] 100% (1/1)79%  (120/152)83%  (18.3/22)
transformBankingDocument (Document): List 100% (1/1)86%  (224/260)86%  (44/51)
<static initializer> 100% (1/1)88%  (23/26)94%  (1.9/2)
parseResources (): List 100% (1/1)95%  (145/152)98%  (37.4/38)
JaxpTextschluesselVerzeichnis (): void 100% (1/1)100% (11/11)100% (4/4)
getDefaultMonitoringThreshold (): Long 100% (1/1)100% (6/6)100% (1/1)
getDefaultReloadIntervalMillis (): Long 100% (1/1)100% (6/6)100% (1/1)
getLocale (): Locale 100% (1/1)100% (6/6)100% (1/1)
getLogger (): Logger 100% (1/1)100% (6/6)100% (1/1)
getMonitoringInfoMessage (Locale, String): String 100% (1/1)100% (12/12)100% (1/1)
getMonitoringThreshold (): long 100% (1/1)100% (11/11)100% (3/3)
getNoSchemaLocationMessage (Locale, String): String 100% (1/1)100% (12/12)100% (1/1)
getNoTextschluesselFoundMessage (Locale): String 100% (1/1)100% (7/7)100% (1/1)
getReloadIntervalMillis (): long 100% (1/1)100% (11/11)100% (3/3)
getResources (): URL [] 100% (1/1)100% (32/32)100% (5/5)
getTaskMonitor (): TaskMonitor 100% (1/1)100% (6/6)100% (1/1)
getTextschluessel (): Textschluessel [] 100% (1/1)100% (10/10)100% (3/3)
getTextschluesselInfoMessage (Locale, Number, Number): String 100% (1/1)100% (16/16)100% (1/1)
getTextschluesselProvider (): JaxpTextschluesselProvider [] 100% (1/1)100% (7/7)100% (1/1)
search (boolean, boolean): Textschluessel [] 100% (1/1)100% (8/8)100% (1/1)
transformTextschluesselDocument (Document): List 100% (1/1)100% (150/150)100% (28/28)

1/*
2 *  jDTAUS Banking RI Textschluesselverzeichnis
3 *  Copyright (C) 2005 Christian Schulte
4 *  <cs@schulte.it>
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2.1 of the License, or any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 *
20 */
21package org.jdtaus.banking.ri.txtdirectory;
22 
23import java.io.File;
24import java.io.IOException;
25import java.io.InputStream;
26import java.net.URI;
27import java.net.URISyntaxException;
28import java.net.URL;
29import java.text.DateFormat;
30import java.text.ParseException;
31import java.text.SimpleDateFormat;
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.Calendar;
35import java.util.Collection;
36import java.util.Date;
37import java.util.HashMap;
38import java.util.HashSet;
39import java.util.Iterator;
40import java.util.LinkedList;
41import java.util.List;
42import java.util.Locale;
43import java.util.Map;
44import javax.xml.parsers.DocumentBuilder;
45import javax.xml.parsers.DocumentBuilderFactory;
46import javax.xml.parsers.ParserConfigurationException;
47import org.jdtaus.banking.Textschluessel;
48import org.jdtaus.banking.TextschluesselVerzeichnis;
49import org.jdtaus.banking.messages.ReadsTextschluesselMessage;
50import org.jdtaus.banking.messages.SearchesTextschluesselMessage;
51import org.jdtaus.core.container.ContainerFactory;
52import org.jdtaus.core.container.PropertyException;
53import org.jdtaus.core.logging.spi.Logger;
54import org.jdtaus.core.monitor.spi.Task;
55import org.jdtaus.core.monitor.spi.TaskMonitor;
56import org.jdtaus.core.sax.util.EntityResolverChain;
57import org.w3c.dom.Document;
58import org.w3c.dom.Element;
59import org.w3c.dom.NodeList;
60import org.xml.sax.ErrorHandler;
61import org.xml.sax.SAXException;
62import org.xml.sax.SAXParseException;
63 
64/**
65 * Textschlüssel directory implementation backed by XML files.
66 * <p>This implementation uses XML resources provided by any available {@link JaxpTextschluesselProvider}
67 * implementation. Resources with a {@code file} URI scheme are monitored for changes by querying the last modification
68 * time. Monitoring is controlled by property {@code reloadIntervalMillis}.</p>
69 *
70 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
71 * @version $JDTAUS: JaxpTextschluesselVerzeichnis.java 8810 2012-12-04 00:45:37Z schulte $
72 */
73public class JaxpTextschluesselVerzeichnis implements TextschluesselVerzeichnis
74{
75 
76    /** JAXP configuration key to the Schema implementation attribute. */
77    private static final String SCHEMA_LANGUAGE_KEY = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
78 
79    /**
80     * JAXP Schema implementation to use.
81     * @see javax.xml.XMLConstants#W3C_XML_SCHEMA_NS_URI
82     */
83    private static final String SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
84 
85    /** jDTAUS {@code textschluessel} namespace URI. */
86    private static final String TEXTSCHLUESSEL_NS = "http://jdtaus.org/banking/xml/textschluessel";
87 
88    /** jDTAUS {@code banking} namespace URI. */
89    private static final String BANKING_NS = "http://jdtaus.org/banking/model";
90 
91    /** {@code http://www.w3.org/2001/XMLSchema-instance} namespace URI. */
92    private static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
93 
94    /** Version supported by this implementation. */
95    private static final String[] SUPPORTED_VERSIONS =
96    {
97        "1.0", "1.1"
98    };
99 
100    /* Flag indicating that initialization has been performed. */
101    private boolean initialized;
102 
103    /** Holds the loaded Textschlüssel instances. */
104    private Textschluessel[] instances;
105 
106    /** Maps {@code File} instances to theire last modification timestamp. */
107    private final Map monitorMap = new HashMap();
108 
109    /** Holds the timestamp resources got checked for modifications. */
110    private long lastCheck = System.currentTimeMillis();
111 
112    /** Number of milliseconds to pass before resources are checked for modifications. */
113    private Long reloadIntervalMillis;
114 
115    /** Number of Textschluessel for which progress monitoring gets enabled. */
116    private Long monitoringThreshold;
117 
118    /**
119     * Creates a new {@code XMLTextschluesselVerzeichnis} instance taking the number of milliseconds to pass before
120     * resources are checked for modifications and the number of Textschluessel for which progress monitoring gets
121     * enabled.
122     *
123     * @param reloadIntervalMillis Number of milliseconds to pass before resources are checked for modifications.
124     * @param monitoringThreshold Number of Textschluessel for which progress monitoring gets enabled.
125     */
126    public JaxpTextschluesselVerzeichnis( final long reloadIntervalMillis, final long monitoringThreshold )
127    {
128        this();
129        if ( reloadIntervalMillis > 0 )
130        {
131            this.reloadIntervalMillis = new Long( reloadIntervalMillis );
132        }
133        if ( monitoringThreshold > 0 )
134        {
135            this.monitoringThreshold = new Long( monitoringThreshold );
136        }
137    }
138 
139    /**
140     * Gets the number of milliseconds to pass before resources are checked for modifications.
141     *
142     * @return The number of milliseconds to pass before resources are checked for modifications.
143     */
144    public long getReloadIntervalMillis()
145    {
146        if ( this.reloadIntervalMillis == null )
147        {
148            this.reloadIntervalMillis = this.getDefaultReloadIntervalMillis();
149        }
150 
151        return this.reloadIntervalMillis.longValue();
152    }
153 
154    /**
155     * Gets the number of Textschluessel for which progress monitoring gets enabled.
156     *
157     * @return The number of Textschluessel for which progress monitoring gets enabled.
158     */
159    public long getMonitoringThreshold()
160    {
161        if ( this.monitoringThreshold == null )
162        {
163            this.monitoringThreshold = this.getDefaultMonitoringThreshold();
164        }
165 
166        return this.monitoringThreshold.longValue();
167    }
168 
169    public Textschluessel[] getTextschluessel()
170    {
171        this.assertValidProperties();
172        this.assertInitialized();
173        return this.searchTextschluessel( null, null, null );
174    }
175 
176    public Textschluessel getTextschluessel( final int key, final int extension )
177    {
178        if ( key < 0 || key > 99 )
179        {
180            throw new IllegalArgumentException( Integer.toString( key ) );
181        }
182        if ( extension < 0 || extension > 999 )
183        {
184            throw new IllegalArgumentException( Integer.toString( extension ) );
185        }
186 
187        this.assertValidProperties();
188        this.assertInitialized();
189 
190        for ( int i = this.instances.length - 1; i >= 0; i-- )
191        {
192            if ( this.instances[i].getKey() == key
193                 && ( this.instances[i].isVariable() || this.instances[i].getExtension() == extension ) )
194            {
195                return (Textschluessel) this.instances[i].clone();
196            }
197        }
198 
199        return null;
200    }
201 
202    public Textschluessel getTextschluessel( final int key, final int extension, final Date date )
203    {
204        if ( key < 0 || key > 99 )
205        {
206            throw new IllegalArgumentException( Integer.toString( key ) );
207        }
208        if ( extension < 0 || extension > 999 )
209        {
210            throw new IllegalArgumentException( Integer.toString( extension ) );
211        }
212        if ( date == null )
213        {
214            throw new NullPointerException( "date" );
215        }
216 
217        this.assertValidProperties();
218        this.assertInitialized();
219 
220        final Textschluessel textschluessel = this.getTextschluessel( key, extension );
221        return textschluessel != null && textschluessel.isValidAt( date ) ? textschluessel : null;
222    }
223 
224    public final Textschluessel[] search( final boolean debit, final boolean remittance )
225    {
226        return this.searchTextschluessel( Boolean.valueOf( debit ), Boolean.valueOf( remittance ), null );
227    }
228 
229    public final Textschluessel[] search( final boolean debit, final boolean remittance, final Date date )
230    {
231        if ( date == null )
232        {
233            throw new NullPointerException( "date" );
234        }
235 
236        return this.searchTextschluessel( Boolean.valueOf( debit ), Boolean.valueOf( remittance ), date );
237    }
238 
239    public Textschluessel[] searchTextschluessel( final Boolean debit, final Boolean remittance, final Date date )
240    {
241        this.assertValidProperties();
242        this.assertInitialized();
243 
244        final Collection col = new ArrayList( this.instances.length );
245 
246        if ( this.instances.length > 0 )
247        {
248            final Task task = new Task();
249            task.setCancelable( true );
250            task.setDescription( new SearchesTextschluesselMessage() );
251            task.setIndeterminate( false );
252            task.setMaximum( this.instances.length - 1 );
253            task.setMinimum( 0 );
254            task.setProgress( 0 );
255 
256            try
257            {
258                if ( task.getMaximum() > this.getMonitoringThreshold() )
259                {
260                    this.getTaskMonitor().monitor( task );
261                }
262 
263                for ( int i = this.instances.length - 1; i >= 0 && !task.isCancelled(); i-- )
264                {
265                    task.setProgress( task.getMaximum() - i );
266 
267                    if ( ( debit == null ? true : this.instances[i].isDebit() == debit.booleanValue() )
268                         && ( remittance == null ? true : this.instances[i].isRemittance() == remittance.booleanValue() )
269                         && ( date == null ? true : this.instances[i].isValidAt( date ) ) )
270                    {
271                        col.add( this.instances[i].clone() );
272                    }
273                }
274 
275                if ( task.isCancelled() )
276                {
277                    col.clear();
278                }
279 
280            }
281            finally
282            {
283                if ( task.getMaximum() > this.getMonitoringThreshold() )
284                {
285                    this.getTaskMonitor().finish( task );
286                }
287            }
288        }
289 
290        return (Textschluessel[]) col.toArray( new Textschluessel[ col.size() ] );
291    }
292 
293    /**
294     * Initializes the instance to hold the parsed XML Textschluessel instances.
295     *
296     * @see #assertValidProperties()
297     * @see #parseResources()
298     * @see #transformDocument(Document)
299     */
300    private synchronized void assertInitialized()
301    {
302        try
303        {
304            if ( System.currentTimeMillis() - this.lastCheck > this.getReloadIntervalMillis()
305                 && !this.monitorMap.isEmpty() )
306            {
307                this.lastCheck = System.currentTimeMillis();
308                for ( final Iterator it = this.monitorMap.entrySet().iterator(); it.hasNext(); )
309                {
310                    final Map.Entry entry = (Map.Entry) it.next();
311                    final File file = (File) entry.getKey();
312                    final Long lastModified = (Long) entry.getValue();
313 
314                    assert lastModified != null : "Expected modification time.";
315 
316                    if ( file.lastModified() != lastModified.longValue() )
317                    {
318                        this.getLogger().info( this.getChangeInfoMessage( this.getLocale(), file.getAbsolutePath() ) );
319                        this.initialized = false;
320                        break;
321                    }
322                }
323            }
324 
325            if ( !this.initialized )
326            {
327                this.monitorMap.clear();
328 
329                final List/*<Document>*/ documents = this.parseResources();
330                final Collection parsedTextschluessel = new LinkedList();
331 
332                for ( final Iterator it = documents.iterator(); it.hasNext(); )
333                {
334                    final Document document = (Document) it.next();
335                    parsedTextschluessel.addAll( this.transformDocument( document ) );
336                }
337 
338                final Map types = new HashMap( parsedTextschluessel.size() );
339                final Collection checked = new ArrayList( parsedTextschluessel.size() );
340 
341                for ( final Iterator it = parsedTextschluessel.iterator(); it.hasNext(); )
342                {
343                    Map keys;
344                    final Textschluessel i = (Textschluessel) it.next();
345                    final Integer key = new Integer( i.getKey() );
346                    final Integer ext = new Integer( i.getExtension() );
347 
348                    if ( ( keys = (Map) types.get( key ) ) == null )
349                    {
350                        keys = new HashMap();
351                        types.put( key, keys );
352                    }
353 
354                    if ( keys.put( ext, i ) != null )
355                    {
356                        throw new IllegalStateException( this.getDuplicateTextschluesselMessage(
357                            this.getLocale(), key, ext ) );
358 
359                    }
360 
361                    checked.add( i );
362                }
363 
364                this.instances = (Textschluessel[]) checked.toArray( new Textschluessel[ checked.size() ] );
365                this.getLogger().info( this.getTextschluesselInfoMessage(
366                    this.getLocale(), new Integer( this.instances.length ), new Integer( documents.size() ) ) );
367 
368                this.initialized = true;
369            }
370        }
371        catch ( final IOException e )
372        {
373            this.initialized = false;
374            throw new RuntimeException( e );
375        }
376        catch ( final SAXException e )
377        {
378            this.initialized = false;
379            throw new RuntimeException( e );
380        }
381        catch ( final ParserConfigurationException e )
382        {
383            this.initialized = false;
384            throw new RuntimeException( e );
385        }
386        catch ( final ParseException e )
387        {
388            this.initialized = false;
389            throw new RuntimeException( e );
390        }
391    }
392 
393    /**
394     * Checks configured properties.
395     *
396     * @throws PropertyException if properties hold invalid values.
397     */
398    private void assertValidProperties()
399    {
400        if ( this.getReloadIntervalMillis() < 0L )
401        {
402            throw new PropertyException( "reloadIntervalMillis", Long.toString( this.getReloadIntervalMillis() ) );
403        }
404        if ( this.getMonitoringThreshold() < 0L )
405        {
406            throw new PropertyException( "monitoringThreshold", Long.toString( this.getMonitoringThreshold() ) );
407        }
408    }
409 
410    /**
411     * Gets XML resources provided by any available {@code TextschluesselProvider} implementation.
412     *
413     * @return XML resources provided by any available {@code TextschluesselProvider} implementation.
414     *
415     * @throws IOException if retrieving the resources fails.
416     *
417     * @see JaxpTextschluesselProvider
418     */
419    private URL[] getResources() throws IOException
420    {
421        final Collection resources = new HashSet();
422        final JaxpTextschluesselProvider[] provider = this.getTextschluesselProvider();
423 
424        for ( int i = provider.length - 1; i >= 0; i-- )
425        {
426            resources.addAll( Arrays.asList( provider[i].getResources() ) );
427        }
428 
429        return (URL[]) resources.toArray( new URL[ resources.size() ] );
430    }
431 
432    /**
433     * Adds a resource to the list of resources to monitor for changes.
434     *
435     * @param url the URL of the resource to monitor for changes.
436     *
437     * @throws NullPointerException if {@code url} is {@code null}.
438     */
439    private void monitorResource( final URL url )
440    {
441        if ( url == null )
442        {
443            throw new NullPointerException( "url" );
444        }
445 
446        try
447        {
448            final File file = new File( new URI( url.toString() ) );
449            this.monitorMap.put( file, new Long( file.lastModified() ) );
450            this.getLogger().info( this.getMonitoringInfoMessage( this.getLocale(), file.getAbsolutePath() ) );
451        }
452        catch ( final IllegalArgumentException e )
453        {
454            this.getLogger().info( this.getNotMonitoringWarningMessage(
455                this.getLocale(), url.toExternalForm(), e.getMessage() ) );
456 
457        }
458        catch ( final URISyntaxException e )
459        {
460            this.getLogger().info( this.getNotMonitoringWarningMessage(
461                this.getLocale(), url.toExternalForm(), e.getMessage() ) );
462 
463        }
464    }
465 
466    /**
467     * Parses all XML resources.
468     *
469     * @return the parsed XML documents.
470     *
471     * @see #getResources()
472     * @see #getDocumentBuilder()
473     *
474     * @throws ParserConfigurationException if configuring the parser fails.
475     * @throws IOException if reading resources fails.
476     * @throws SAXException if parsing fails.
477     */
478    private List/*<Document>*/ parseResources() throws ParserConfigurationException, IOException, SAXException
479    {
480        InputStream stream = null;
481 
482        final URL[] resources = this.getResources();
483        final List documents = new LinkedList();
484 
485        if ( resources.length > 0 )
486        {
487            final DocumentBuilder validatingParser = this.getDocumentBuilder();
488            final DocumentBuilderFactory namespaceAwareFactory = DocumentBuilderFactory.newInstance();
489 
490            namespaceAwareFactory.setNamespaceAware( true );
491            final DocumentBuilder nonValidatingParser = namespaceAwareFactory.newDocumentBuilder();
492 
493            final Task task = new Task();
494            task.setCancelable( false );
495            task.setDescription( new ReadsTextschluesselMessage() );
496            task.setIndeterminate( false );
497            task.setMaximum( resources.length - 1 );
498            task.setMinimum( 0 );
499            task.setProgress( 0 );
500 
501            try
502            {
503                this.getTaskMonitor().monitor( task );
504 
505                for ( int i = resources.length - 1; i >= 0; i-- )
506                {
507                    task.setProgress( task.getMaximum() - i );
508                    final URL resource = resources[i];
509                    final ErrorHandler errorHandler = new ErrorHandler()
510                    {
511 
512                        public void warning( final SAXParseException e )
513                            throws SAXException
514                        {
515                            getLogger().warn( getParseExceptionMessage(
516                                getLocale(), resource.toExternalForm(),
517                                e.getMessage(), new Integer( e.getLineNumber() ),
518                                new Integer( e.getColumnNumber() ) ) );
519 
520                        }
521 
522                        public void error( final SAXParseException e )
523                            throws SAXException
524                        {
525                            throw new SAXException( getParseExceptionMessage(
526                                getLocale(), resource.toExternalForm(),
527                                e.getMessage(), new Integer( e.getLineNumber() ),
528                                new Integer( e.getColumnNumber() ) ), e );
529 
530                        }
531 
532                        public void fatalError( final SAXParseException e )
533                            throws SAXException
534                        {
535                            throw new SAXException( getParseExceptionMessage(
536                                getLocale(), resource.toExternalForm(),
537                                e.getMessage(), new Integer( e.getLineNumber() ),
538                                new Integer( e.getColumnNumber() ) ), e );
539 
540                        }
541 
542                    };
543 
544                    nonValidatingParser.setErrorHandler( errorHandler );
545                    validatingParser.setErrorHandler( errorHandler );
546 
547                    this.monitorResource( resource );
548                    stream = resource.openStream();
549                    Document doc = nonValidatingParser.parse( stream );
550                    if ( doc.getDocumentElement().hasAttributeNS( XSI_NS, "schemaLocation" ) )
551                    {
552                        stream.close();
553                        stream = resource.openStream();
554                        doc = validatingParser.parse( stream );
555                    }
556                    else if ( this.getLogger().isInfoEnabled() )
557                    {
558                        this.getLogger().info(
559                            this.getNoSchemaLocationMessage( this.getLocale(), resource.toExternalForm() ) );
560                    }
561 
562                    documents.add( doc );
563                    stream.close();
564                }
565            }
566            finally
567            {
568                this.getTaskMonitor().finish( task );
569            }
570        }
571        else
572        {
573            this.getLogger().warn( this.getNoTextschluesselFoundMessage( this.getLocale() ) );
574        }
575 
576        return documents;
577    }
578 
579    /**
580     * Transforms a document to the Textschluessel instances it contains.
581     *
582     * @param doc the document to transform.
583     *
584     * @return an array of Textschluessel instances from the given document.
585     *
586     * @throws IllegalArgumentException if {@code doc} cannot be transformed.
587     * @throws ParseException if parsing fails.
588     *
589     * @see #transformTextschluesselDocument(Document)
590     * @see #transformBankingDocument(Document)
591     */
592    private List/*<Textschluessel>*/ transformDocument( final Document doc ) throws ParseException
593    {
594        String modelVersion = null;
595        final String namespace = doc.getDocumentElement().getNamespaceURI();
596 
597        if ( namespace == null )
598        {
599            throw new RuntimeException( this.getUnsupportedNamespaceMessage( this.getLocale(), namespace ) );
600        }
601        else if ( TEXTSCHLUESSEL_NS.equals( namespace ) )
602        {
603            modelVersion = doc.getDocumentElement().getAttributeNS( namespace, "version" );
604        }
605        else if ( BANKING_NS.equals( namespace ) )
606        {
607            modelVersion = doc.getDocumentElement().getAttributeNS( namespace, "modelVersion" );
608        }
609        else
610        {
611            throw new RuntimeException( this.getUnsupportedNamespaceMessage( this.getLocale(), namespace ) );
612        }
613 
614        boolean supportedModelVersion = false;
615        for ( int i = SUPPORTED_VERSIONS.length - 1; i >= 0; i-- )
616        {
617            if ( SUPPORTED_VERSIONS[i].equals( modelVersion ) )
618            {
619                supportedModelVersion = true;
620                break;
621            }
622        }
623 
624        if ( !supportedModelVersion )
625        {
626            throw new RuntimeException( this.getUnsupportedModelVersionMessage( this.getLocale(), modelVersion ) );
627        }
628 
629        final List textschluessel = new LinkedList();
630 
631        if ( namespace.equals( TEXTSCHLUESSEL_NS ) )
632        {
633            textschluessel.addAll( this.transformTextschluesselDocument( doc ) );
634        }
635        else if ( namespace.equals( BANKING_NS ) )
636        {
637            textschluessel.addAll( this.transformBankingDocument( doc ) );
638        }
639 
640        return textschluessel;
641    }
642 
643    /**
644     * Transforms a document from deprecated {@code textschluessel} namespace to the {@code Textschluessel} instances it
645     * contains.
646     *
647     * @param doc the document to transform.
648     *
649     * @return an list of Textschluessel instances from the given document.
650     *
651     * @throws IllegalArgumentException if {@code doc} contains invalid content.
652     */
653    private List/*<Textschluessel>*/ transformTextschluesselDocument( final Document doc )
654    {
655        final List list = new LinkedList();
656        final NodeList typeList = doc.getDocumentElement().getElementsByTagNameNS(
657            TEXTSCHLUESSEL_NS, "transactionTypes" );
658 
659        for ( int i = typeList.getLength() - 1; i >= 0; i-- )
660        {
661            final Element parent = (Element) typeList.item( i );
662            if ( parent.getParentNode().equals( doc.getDocumentElement() ) )
663            {
664                final NodeList type = parent.getElementsByTagNameNS( TEXTSCHLUESSEL_NS, "transactionType" );
665                for ( int t = type.getLength() - 1; t >= 0; t-- )
666                {
667                    final Element e = (Element) type.item( t );
668                    if ( e.getParentNode().equals( parent ) )
669                    {
670                        final Textschluessel textschluessel = new Textschluessel();
671                        list.add( textschluessel );
672 
673                        final String textschluesselType = e.getAttributeNS( TEXTSCHLUESSEL_NS, "type" );
674                        textschluessel.setDebit( "DEBIT".equals( textschluesselType ) );
675                        textschluessel.setRemittance( "REMITTANCE".equals( textschluesselType ) );
676                        textschluessel.setKey( Integer.valueOf(
677                            e.getAttributeNS( TEXTSCHLUESSEL_NS, "key" ) ).intValue() );
678 
679                        final String extension = e.getAttributeNS( TEXTSCHLUESSEL_NS, "extension" );
680                        if ( "VARIABLE".equals( extension ) )
681                        {
682                            textschluessel.setVariable( true );
683                            textschluessel.setExtension( 0 );
684                        }
685                        else
686                        {
687                            textschluessel.setExtension( Integer.valueOf( extension ).intValue() );
688                        }
689 
690                        final NodeList descriptions = e.getElementsByTagNameNS( TEXTSCHLUESSEL_NS, "description" );
691                        for ( int d = descriptions.getLength() - 1; d >= 0; d-- )
692                        {
693                            final Element description = (Element) descriptions.item( d );
694 
695                            if ( description.getParentNode().equals( e ) )
696                            {
697                                final String language = description.getAttributeNS( TEXTSCHLUESSEL_NS, "language" );
698                                final String text = description.getFirstChild().getNodeValue();
699                                textschluessel.setShortDescription( new Locale( language.toLowerCase() ), text );
700                            }
701                        }
702                    }
703                }
704            }
705        }
706 
707        return list;
708    }
709 
710    /**
711     * Transforms a document from the {@code banking} namespace to the {@code Textschluessel} instances it contains.
712     *
713     * @param doc the document to transform.
714     *
715     * @return an list of Textschluessel instances from the given document.
716     *
717     * @throws IllegalArgumentException if {@code doc} contains invalid content.
718     * @throws ParseException if parsing fails.
719     */
720    private List/*<Textschluessel>*/ transformBankingDocument( final Document doc ) throws ParseException
721    {
722        final List list = new LinkedList();
723        final Calendar cal = Calendar.getInstance();
724        final DateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd" );
725        final String systemLanguage = Locale.getDefault().getLanguage().toLowerCase();
726 
727        final NodeList typeList = doc.getDocumentElement().getElementsByTagNameNS( BANKING_NS, "textschluessel" );
728        for ( int i = typeList.getLength() - 1; i >= 0; i-- )
729        {
730            final Element e = (Element) typeList.item( i );
731            if ( e.getParentNode().equals( doc.getDocumentElement() ) )
732            {
733                final Textschluessel textschluessel = new Textschluessel();
734                list.add( textschluessel );
735 
736                textschluessel.setKey( Integer.valueOf( e.getAttributeNS( BANKING_NS, "key" ) ).intValue() );
737                if ( e.hasAttributeNS( BANKING_NS, "extension" ) )
738                {
739                    textschluessel.setExtension( Integer.valueOf(
740                        e.getAttributeNS( BANKING_NS, "extension" ) ).intValue() );
741 
742                }
743 
744                textschluessel.setDebit( Boolean.valueOf( e.getAttributeNS( BANKING_NS, "debit" ) ).booleanValue() );
745                textschluessel.setRemittance( Boolean.valueOf(
746                    e.getAttributeNS( BANKING_NS, "remittance" ) ).booleanValue() );
747 
748                textschluessel.setVariable( Boolean.valueOf(
749                    e.getAttributeNS( BANKING_NS, "variableExtension" ) ).booleanValue() );
750 
751                final NodeList texts = e.getElementsByTagNameNS( BANKING_NS, "texts" );
752                if ( e.hasAttributeNS( BANKING_NS, "validFrom" ) )
753                {
754                    cal.setTime( dateFormat.parse( e.getAttributeNS( BANKING_NS, "validFrom" ) ) );
755                    cal.set( Calendar.HOUR_OF_DAY, 0 );
756                    cal.set( Calendar.MINUTE, 0 );
757                    cal.set( Calendar.SECOND, 0 );
758                    cal.set( Calendar.MILLISECOND, 0 );
759                    textschluessel.setValidFrom( cal.getTime() );
760                }
761 
762                if ( e.hasAttributeNS( BANKING_NS, "validTo" ) )
763                {
764                    cal.setTime( dateFormat.parse( e.getAttributeNS( BANKING_NS, "validTo" ) ) );
765                    cal.set( Calendar.HOUR_OF_DAY, 0 );
766                    cal.set( Calendar.MINUTE, 0 );
767                    cal.set( Calendar.SECOND, 0 );
768                    cal.set( Calendar.MILLISECOND, 0 );
769                    textschluessel.setValidTo( cal.getTime() );
770                }
771 
772                for ( int t = texts.getLength() - 1; t >= 0; t-- )
773                {
774                    final Element textsElement = (Element) texts.item( t );
775                    if ( textsElement.getParentNode().equals( e ) )
776                    {
777                        final String defaultLanguage =
778                            textsElement.getAttributeNS( BANKING_NS, "defaultLanguage" ).toLowerCase();
779 
780                        boolean hasSystemLanguage = false;
781                        String defaultText = null;
782 
783                        final NodeList l = textsElement.getElementsByTagNameNS( BANKING_NS, "text" );
784 
785                        for ( int d = l.getLength() - 1; d >= 0; d-- )
786                        {
787                            final Element description = (Element) l.item( d );
788                            if ( description.getParentNode().equals( textsElement ) )
789                            {
790                                final String language = description.getAttributeNS(
791                                    BANKING_NS, "language" ).toLowerCase();
792 
793                                final String text = description.getFirstChild().getNodeValue();
794 
795                                if ( language.equals( defaultLanguage ) )
796                                {
797                                    defaultText = text;
798                                }
799 
800                                if ( systemLanguage.equals( language ) )
801                                {
802                                    hasSystemLanguage = true;
803                                }
804 
805                                textschluessel.setShortDescription( new Locale( language ), text );
806                            }
807                        }
808 
809                        if ( !hasSystemLanguage )
810                        {
811                            textschluessel.setShortDescription( new Locale( systemLanguage ), defaultText );
812                        }
813                    }
814                }
815            }
816        }
817 
818        return list;
819    }
820 
821    /**
822     * Creates a new {@code DocumentBuilder} to use for parsing the XML resources.
823     * <p>This method tries to set the following JAXP property on the system's default XML parser:
824     * <ul>
825     * <li>{@code http://java.sun.com/xml/jaxp/properties/schemaLanguage} set to
826     * {@code http://www.w3.org/2001/XMLSchema}</li>
827     * </ul>When setting this property fails, a non-validating {@code DocumentBuilder} is returned and a warning message
828     * is logged.</p>
829     *
830     * @return a new {@code DocumentBuilder} to be used for parsing resources.
831     *
832     * @throws ParserConfigurationException if configuring the XML parser fails.
833     */
834    private DocumentBuilder getDocumentBuilder() throws ParserConfigurationException
835    {
836        final DocumentBuilder xmlBuilder;
837        final DocumentBuilderFactory xmlFactory = DocumentBuilderFactory.newInstance();
838        xmlFactory.setNamespaceAware( true );
839 
840        try
841        {
842            xmlFactory.setValidating( true );
843            xmlFactory.setAttribute( SCHEMA_LANGUAGE_KEY, SCHEMA_LANGUAGE );
844        }
845        catch ( IllegalArgumentException e )
846        {
847            this.getLogger().info( this.getNoJAXPValidationWarningMessage( this.getLocale(), e.getMessage() ) );
848            xmlFactory.setValidating( false );
849        }
850 
851        xmlBuilder = xmlFactory.newDocumentBuilder();
852        xmlBuilder.setEntityResolver( new EntityResolverChain() );
853        return xmlBuilder;
854    }
855 
856    //--Constructors------------------------------------------------------------
857 
858// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
859    // This section is managed by jdtaus-container-mojo.
860 
861    /** Standard implementation constructor <code>org.jdtaus.banking.ri.txtdirectory.JaxpTextschluesselVerzeichnis</code>. */
862    public JaxpTextschluesselVerzeichnis()
863    {
864        super();
865    }
866 
867// </editor-fold>//GEN-END:jdtausConstructors
868 
869    //------------------------------------------------------------Constructors--
870    //--Dependencies------------------------------------------------------------
871 
872// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
873    // This section is managed by jdtaus-container-mojo.
874 
875    /**
876     * Gets the configured <code>Logger</code> implementation.
877     *
878     * @return The configured <code>Logger</code> implementation.
879     */
880    private Logger getLogger()
881    {
882        return (Logger) ContainerFactory.getContainer().
883            getDependency( this, "Logger" );
884 
885    }
886 
887    /**
888     * Gets the configured <code>TextschluesselProvider</code> implementation.
889     *
890     * @return The configured <code>TextschluesselProvider</code> implementation.
891     */
892    private JaxpTextschluesselProvider[] getTextschluesselProvider()
893    {
894        return (JaxpTextschluesselProvider[]) ContainerFactory.getContainer().
895            getDependency( this, "TextschluesselProvider" );
896 
897    }
898 
899    /**
900     * Gets the configured <code>TaskMonitor</code> implementation.
901     *
902     * @return The configured <code>TaskMonitor</code> implementation.
903     */
904    private TaskMonitor getTaskMonitor()
905    {
906        return (TaskMonitor) ContainerFactory.getContainer().
907            getDependency( this, "TaskMonitor" );
908 
909    }
910 
911    /**
912     * Gets the configured <code>Locale</code> implementation.
913     *
914     * @return The configured <code>Locale</code> implementation.
915     */
916    private Locale getLocale()
917    {
918        return (Locale) ContainerFactory.getContainer().
919            getDependency( this, "Locale" );
920 
921    }
922 
923// </editor-fold>//GEN-END:jdtausDependencies
924 
925    //------------------------------------------------------------Dependencies--
926    //--Properties--------------------------------------------------------------
927 
928// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
929    // This section is managed by jdtaus-container-mojo.
930 
931    /**
932     * Gets the value of property <code>defaultReloadIntervalMillis</code>.
933     *
934     * @return Default number of milliseconds to pass before resources are checked for modifications.
935     */
936    private java.lang.Long getDefaultReloadIntervalMillis()
937    {
938        return (java.lang.Long) ContainerFactory.getContainer().
939            getProperty( this, "defaultReloadIntervalMillis" );
940 
941    }
942 
943    /**
944     * Gets the value of property <code>defaultMonitoringThreshold</code>.
945     *
946     * @return Default number of Textschlüssel for which progress monitoring gets enabled.
947     */
948    private java.lang.Long getDefaultMonitoringThreshold()
949    {
950        return (java.lang.Long) ContainerFactory.getContainer().
951            getProperty( this, "defaultMonitoringThreshold" );
952 
953    }
954 
955// </editor-fold>//GEN-END:jdtausProperties
956 
957    //--------------------------------------------------------------Properties--
958    //--Messages----------------------------------------------------------------
959 
960// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
961    // This section is managed by jdtaus-container-mojo.
962 
963    /**
964     * Gets the text of message <code>noJAXPValidationWarning</code>.
965     * <blockquote><pre>Keine JAXP Validierung verfügbar. {0}</pre></blockquote>
966     * <blockquote><pre>No JAXP validation available. {0}</pre></blockquote>
967     *
968     * @param locale The locale of the message instance to return.
969     * @param detailMessage format parameter.
970     *
971     * @return the text of message <code>noJAXPValidationWarning</code>.
972     */
973    private String getNoJAXPValidationWarningMessage( final Locale locale,
974            final java.lang.String detailMessage )
975    {
976        return ContainerFactory.getContainer().
977            getMessage( this, "noJAXPValidationWarning", locale,
978                new Object[]
979                {
980                    detailMessage
981                });
982 
983    }
984 
985    /**
986     * Gets the text of message <code>notMonitoringWarning</code>.
987     * <blockquote><pre>{0} kann bei Änderung nicht automatisch neu geladen werden. {1}</pre></blockquote>
988     * <blockquote><pre>{0} cannot be monitored. {1}</pre></blockquote>
989     *
990     * @param locale The locale of the message instance to return.
991     * @param resourceName format parameter.
992     * @param detailMessage format parameter.
993     *
994     * @return the text of message <code>notMonitoringWarning</code>.
995     */
996    private String getNotMonitoringWarningMessage( final Locale locale,
997            final java.lang.String resourceName,
998            final java.lang.String detailMessage )
999    {
1000        return ContainerFactory.getContainer().
1001            getMessage( this, "notMonitoringWarning", locale,
1002                new Object[]
1003                {
1004                    resourceName,
1005                    detailMessage
1006                });
1007 
1008    }
1009 
1010    /**
1011     * Gets the text of message <code>changeInfo</code>.
1012     * <blockquote><pre>{0} aktualisiert.</pre></blockquote>
1013     * <blockquote><pre>{0} changed.</pre></blockquote>
1014     *
1015     * @param locale The locale of the message instance to return.
1016     * @param resourceName format parameter.
1017     *
1018     * @return the text of message <code>changeInfo</code>.
1019     */
1020    private String getChangeInfoMessage( final Locale locale,
1021            final java.lang.String resourceName )
1022    {
1023        return ContainerFactory.getContainer().
1024            getMessage( this, "changeInfo", locale,
1025                new Object[]
1026                {
1027                    resourceName
1028                });
1029 
1030    }
1031 
1032    /**
1033     * Gets the text of message <code>monitoringInfo</code>.
1034     * <blockquote><pre>{0} wird bei Änderung automatisch neu geladen.</pre></blockquote>
1035     * <blockquote><pre>Monitoring {0} for changes.</pre></blockquote>
1036     *
1037     * @param locale The locale of the message instance to return.
1038     * @param resourceName format parameter.
1039     *
1040     * @return the text of message <code>monitoringInfo</code>.
1041     */
1042    private String getMonitoringInfoMessage( final Locale locale,
1043            final java.lang.String resourceName )
1044    {
1045        return ContainerFactory.getContainer().
1046            getMessage( this, "monitoringInfo", locale,
1047                new Object[]
1048                {
1049                    resourceName
1050                });
1051 
1052    }
1053 
1054    /**
1055     * Gets the text of message <code>textschluesselInfo</code>.
1056     * <blockquote><pre>{1,choice,0#Kein Dokument|1#Ein Dokument|1<{1} Dokumente} gelesen. {0,choice,0#Keine|1#Einen|1<{0}} Textschlüssel verarbeitet.</pre></blockquote>
1057     * <blockquote><pre>Read {1,choice,0#no document|1#one document|1<{1} documents}. Processed {0,choice,0#no entities|1#one entity|1<{0} entities}.</pre></blockquote>
1058     *
1059     * @param locale The locale of the message instance to return.
1060     * @param entityCount format parameter.
1061     * @param documentCount format parameter.
1062     *
1063     * @return the text of message <code>textschluesselInfo</code>.
1064     */
1065    private String getTextschluesselInfoMessage( final Locale locale,
1066            final java.lang.Number entityCount,
1067            final java.lang.Number documentCount )
1068    {
1069        return ContainerFactory.getContainer().
1070            getMessage( this, "textschluesselInfo", locale,
1071                new Object[]
1072                {
1073                    entityCount,
1074                    documentCount
1075                });
1076 
1077    }
1078 
1079    /**
1080     * Gets the text of message <code>unsupportedNamespace</code>.
1081     * <blockquote><pre>Ungültiger XML-Namensraum {0}.</pre></blockquote>
1082     * <blockquote><pre>Unsupported XML namespace {0}.</pre></blockquote>
1083     *
1084     * @param locale The locale of the message instance to return.
1085     * @param namespace format parameter.
1086     *
1087     * @return the text of message <code>unsupportedNamespace</code>.
1088     */
1089    private String getUnsupportedNamespaceMessage( final Locale locale,
1090            final java.lang.String namespace )
1091    {
1092        return ContainerFactory.getContainer().
1093            getMessage( this, "unsupportedNamespace", locale,
1094                new Object[]
1095                {
1096                    namespace
1097                });
1098 
1099    }
1100 
1101    /**
1102     * Gets the text of message <code>unsupportedModelVersion</code>.
1103     * <blockquote><pre>Keine Unterstützung für Modellversion {0}.</pre></blockquote>
1104     * <blockquote><pre>Unsupported model version {0}.</pre></blockquote>
1105     *
1106     * @param locale The locale of the message instance to return.
1107     * @param modelVersion format parameter.
1108     *
1109     * @return the text of message <code>unsupportedModelVersion</code>.
1110     */
1111    private String getUnsupportedModelVersionMessage( final Locale locale,
1112            final java.lang.String modelVersion )
1113    {
1114        return ContainerFactory.getContainer().
1115            getMessage( this, "unsupportedModelVersion", locale,
1116                new Object[]
1117                {
1118                    modelVersion
1119                });
1120 
1121    }
1122 
1123    /**
1124     * Gets the text of message <code>parseException</code>.
1125     * <blockquote><pre>Fehler bei der Verarbeitung der Resource "{0}" in Zeile {2}, Spalte {3}. {1}</pre></blockquote>
1126     * <blockquote><pre>Error parsing resource "{0}" at line {2}, column {3}. {1}</pre></blockquote>
1127     *
1128     * @param locale The locale of the message instance to return.
1129     * @param resourceName format parameter.
1130     * @param cause format parameter.
1131     * @param line format parameter.
1132     * @param column format parameter.
1133     *
1134     * @return the text of message <code>parseException</code>.
1135     */
1136    private String getParseExceptionMessage( final Locale locale,
1137            final java.lang.String resourceName,
1138            final java.lang.String cause,
1139            final java.lang.Number line,
1140            final java.lang.Number column )
1141    {
1142        return ContainerFactory.getContainer().
1143            getMessage( this, "parseException", locale,
1144                new Object[]
1145                {
1146                    resourceName,
1147                    cause,
1148                    line,
1149                    column
1150                });
1151 
1152    }
1153 
1154    /**
1155     * Gets the text of message <code>noSchemaLocation</code>.
1156     * <blockquote><pre>Kein schemaLocation Attribut in Ressource "{0}". Keine Schema-Validierung.</pre></blockquote>
1157     * <blockquote><pre>No schemaLocation attribute in resource "{0}". Schema validation skipped.</pre></blockquote>
1158     *
1159     * @param locale The locale of the message instance to return.
1160     * @param resource format parameter.
1161     *
1162     * @return the text of message <code>noSchemaLocation</code>.
1163     */
1164    private String getNoSchemaLocationMessage( final Locale locale,
1165            final java.lang.String resource )
1166    {
1167        return ContainerFactory.getContainer().
1168            getMessage( this, "noSchemaLocation", locale,
1169                new Object[]
1170                {
1171                    resource
1172                });
1173 
1174    }
1175 
1176    /**
1177     * Gets the text of message <code>duplicateTextschluessel</code>.
1178     * <blockquote><pre>Textschlüssel {0,number,00}{1,number,000}  ist mehrfach vorhanden.</pre></blockquote>
1179     * <blockquote><pre>Non-unique Textschluessel {0,number,00}{1,number,000}.</pre></blockquote>
1180     *
1181     * @param locale The locale of the message instance to return.
1182     * @param key format parameter.
1183     * @param extension format parameter.
1184     *
1185     * @return the text of message <code>duplicateTextschluessel</code>.
1186     */
1187    private String getDuplicateTextschluesselMessage( final Locale locale,
1188            final java.lang.Number key,
1189            final java.lang.Number extension )
1190    {
1191        return ContainerFactory.getContainer().
1192            getMessage( this, "duplicateTextschluessel", locale,
1193                new Object[]
1194                {
1195                    key,
1196                    extension
1197                });
1198 
1199    }
1200 
1201    /**
1202     * Gets the text of message <code>noTextschluesselFound</code>.
1203     * <blockquote><pre>Keine Textschlüssel gefunden.</pre></blockquote>
1204     * <blockquote><pre>No Textschlüssel found.</pre></blockquote>
1205     *
1206     * @param locale The locale of the message instance to return.
1207     *
1208     * @return the text of message <code>noTextschluesselFound</code>.
1209     */
1210    private String getNoTextschluesselFoundMessage( final Locale locale )
1211    {
1212        return ContainerFactory.getContainer().
1213            getMessage( this, "noTextschluesselFound", locale, null );
1214 
1215    }
1216 
1217// </editor-fold>//GEN-END:jdtausMessages
1218 
1219    //----------------------------------------------------------------Messages--
1220}

[all classes][org.jdtaus.banking.ri.txtdirectory]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov