View Javadoc
1   /*
2    * Copyright 2007 Joachim Grueneis
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 org.castor.xml;
17  
18  import java.beans.PropertyChangeListener;
19  import java.beans.PropertyChangeSupport;
20  import java.io.IOException;
21  import java.io.OutputStream;
22  import java.io.Writer;
23  
24  import javax.inject.Inject;
25  import javax.xml.parsers.SAXParser;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.castor.core.util.Messages;
30  import org.castor.mapping.BindingType;
31  import org.castor.mapping.MappingUnmarshaller;
32  import org.exolab.castor.mapping.Mapping;
33  import org.exolab.castor.mapping.MappingException;
34  import org.exolab.castor.mapping.MappingLoader;
35  import org.exolab.castor.util.RegExpEvaluator;
36  import org.exolab.castor.xml.AbstractXMLNaming;
37  import org.exolab.castor.xml.Introspector;
38  import org.exolab.castor.xml.NodeType;
39  import org.exolab.castor.xml.OutputFormat;
40  import org.exolab.castor.xml.ResolverException;
41  import org.exolab.castor.xml.Serializer;
42  import org.exolab.castor.xml.XMLClassDescriptorResolver;
43  import org.exolab.castor.xml.XMLContext;
44  import org.exolab.castor.xml.XMLSerializerFactory;
45  import org.exolab.castor.xml.util.DefaultNaming;
46  import org.exolab.castor.xml.util.ResolverStrategy;
47  import org.exolab.castor.xml.util.XMLParserUtils;
48  import org.xml.sax.DocumentHandler;
49  import org.xml.sax.Parser;
50  import org.xml.sax.SAXException;
51  import org.xml.sax.XMLReader;
52  
53  /**
54   * The internal context is meant as center piece providing (and keeping) all
55   * information that is required by Marshaller, Unmarshaller, SourceGenerator,
56   * MappingTool, SchemaReader and SchemaWriter. It is created, filled with
57   * initial data and put into all other parts of Castor by {@link XMLContext}. It
58   * is NOT meant to be directly instantiated by user implementations! For all
59   * other objects it provides access to Castor state information (e.g. known
60   * descriptors) and configuration values.
61   * 
62   * @author <a href="mailto:jgrueneis At gmail DOT com">Joachim Grueneis</a>
63   * @since 1.1.2
64   */
65  public abstract class AbstractInternalContext implements InternalContext {
66  
67     private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
68  
69     /** Logger to be used. */
70     private static final Log LOG = LogFactory.getFactory().getInstance(AbstractInternalContext.class);
71  
72     /** The properties to use internally to provide parser, serializer, ... */
73     private org.castor.core.util.AbstractProperties _properties;
74  
75     /**
76      * {@link XMLClassDescriptorResolver} instance used for caching XML-related
77      * class descriptors.
78      */
79     private XMLClassDescriptorResolver _xmlClassDescriptorResolver;
80  
81     /**
82      * The XMLContext knows the one Introspector to be used.
83      */
84     private Introspector _introspector;
85  
86     /**
87      * The XMLClassDescriptor resolver strategy to use.
88      */
89     private ResolverStrategy _resolverStrategy;
90  
91     /**
92      * The {@link MappingLoader} to use.
93      */
94     private MappingLoader _mappingLoader;
95  
96     /**
97      * The {@link AbstractXMLNaming} to be used.
98      */
99     private XMLNaming _xmlNaming;
100 
101    /**
102     * The {@link JavaNaming} to be used.
103     */
104    @Inject
105    private JavaNaming _javaNaming;
106 
107    /**
108     * The class loader to use.
109     */
110    private ClassLoader _classLoader;
111 
112    /**
113     * The {@link NodeType} to use for primitives.
114     */
115    private NodeType _primitiveNodeType;
116 
117    /**
118     * The {@link RegExpevaluator}?to use.
119     */
120    private RegExpEvaluator _regExpEvaluator;
121 
122    /**
123     * Creates an instance of {@link AbstractInternalContext}. The internal
124     * context is meant to hold the configuration and state informations, but not
125     * necessarily retrieving those values...
126     */
127    public AbstractInternalContext() {
128       _properties = XMLProperties.newInstance();
129       // TODO[WG]: remove once injection works
130       _javaNaming = new JavaNamingImpl(this);
131    }
132 
133    /**
134     * @see org.castor.xml.InternalContext#addMapping(org.exolab.castor.mapping.Mapping)
135     */
136    public void addMapping(final Mapping mapping) throws MappingException {
137       MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
138       MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader(mapping, BindingType.XML);
139       _xmlClassDescriptorResolver.setMappingLoader(mappingLoader);
140    }
141 
142    /**
143     * @see org.castor.xml.InternalContext#addClass(java.lang.Class)
144     */
145    public void addClass(final Class clazz) throws ResolverException {
146       _xmlClassDescriptorResolver.addClass(clazz);
147    }
148 
149    /**
150     * @see org.castor.xml.InternalContext#addClasses(java.lang.Class[])
151     */
152    public void addClasses(final Class[] clazzes) throws ResolverException {
153       _xmlClassDescriptorResolver.addClasses(clazzes);
154    }
155 
156    /**
157     * @see org.castor.xml.InternalContext#addPackage(java.lang.String)
158     */
159    public void addPackage(final String packageName) throws ResolverException {
160       _xmlClassDescriptorResolver.addPackage(packageName);
161    }
162 
163    /**
164     * @see org.castor.xml.InternalContext#addPackages(java.lang.String[])
165     */
166    public void addPackages(final String[] packageNames) throws ResolverException {
167       _xmlClassDescriptorResolver.addPackages(packageNames);
168    }
169 
170    /**
171     * @see org.castor.xml.InternalContext#setResolver(org.exolab.castor.xml.XMLClassDescriptorResolver)
172     */
173    public void setResolver(final XMLClassDescriptorResolver xmlClassDescriptorResolver) {
174       this._xmlClassDescriptorResolver = xmlClassDescriptorResolver;
175    }
176 
177    /**
178     * {@inheritDoc}
179     * 
180     * @see org.castor.xml.InternalContext#setProperty(java.lang.String,
181     *      java.lang.Object)
182     */
183    public void setProperty(final String propertyName, final Object value) {
184       // resetting all values that are only reinitialized if null
185       if (propertyName == null) {
186          IllegalArgumentException iae = new IllegalArgumentException(
187                "setProperty must not be called with a propertyName == null");
188          LOG.warn(iae.getMessage());
189          throw iae;
190       }
191       if (propertyName.equals(XMLProperties.XML_NAMING)) {
192          if (value instanceof String) {
193             setXMLNaming((String) value);
194          } else if (value instanceof XMLNaming) {
195             setXMLNaming((XMLNaming) value);
196          } else {
197             IllegalArgumentException iae = new IllegalArgumentException(
198                   "XML Naming can only be set to a String or an implementation of XMLNaming");
199             LOG.warn(iae.getMessage());
200             throw iae;
201          }
202       }
203       if (propertyName.equals(XMLProperties.JAVA_NAMING)) {
204          if (value instanceof String) {
205             setJavaNaming((String) value);
206          } else if (value instanceof JavaNaming) {
207             setJavaNaming((JavaNaming) value);
208          } else {
209             IllegalArgumentException iae = new IllegalArgumentException(
210                   "Java Naming can only be set to a String or an implementation of JavaNaming");
211             LOG.warn(iae.getMessage());
212             throw iae;
213          }
214       }
215       _primitiveNodeType = null;
216       _regExpEvaluator = null;
217 
218       // now writing the new property
219       this.setPropertyInternal(propertyName, value);
220    }
221 
222    /**
223     * @see org.castor.xml.InternalContext#getProperty(java.lang.String)
224     */
225    public Object getProperty(final String propertyName) {
226       return _properties.getObject(propertyName);
227    }
228 
229    /**
230     * {@inheritDoc}
231     * 
232     * @see org.castor.xml.InternalContext#getXMLNaming()
233     */
234    public XMLNaming getXMLNaming() {
235       if (_xmlNaming != null) {
236          return _xmlNaming;
237       }
238 
239       String prop = _properties.getString(XMLProperties.XML_NAMING, null);
240       setXMLNaming(prop);
241       return _xmlNaming;
242    }
243 
244    /**
245     * {@inheritDoc}
246     * 
247     * @see org.castor.xml.InternalContext#getXMLNaming(java.lang.ClassLoader)
248     * @deprecated Makes no sence!
249     */
250    public XMLNaming getXMLNaming(final ClassLoader classLoader) {
251       return getXMLNaming();
252    } // -- getXMLNaming
253 
254    /**
255     * @see org.castor.xml.InternalContext#getJavaNaming()
256     */
257    public JavaNaming getJavaNaming() {
258       return _javaNaming;
259    }
260 
261    /**
262     * @see org.castor.xml.InternalContext#getParser()
263     */
264    public Parser getParser() {
265       return getParser(null);
266    }
267 
268    /**
269     * @see org.castor.xml.InternalContext#getParser(java.lang.String)
270     */
271    public Parser getParser(final String features) {
272       return XMLParserUtils.getParser(_properties, features);
273    }
274 
275    /**
276     * @see org.castor.xml.InternalContext#getXMLReader()
277     */
278    public XMLReader getXMLReader() {
279       return getXMLReader(null);
280    }
281 
282    /**
283     * @see org.castor.xml.InternalContext#getXMLReader(java.lang.String)
284     */
285    public XMLReader getXMLReader(final String features) {
286       XMLReader reader = null;
287       Boolean validation = _properties.getBoolean(XMLProperties.PARSER_VALIDATION);
288       Boolean namespaces = _properties.getBoolean(XMLProperties.NAMESPACES);
289 
290       String readerClassName = _properties.getString(XMLProperties.PARSER);
291 
292       if (readerClassName == null || readerClassName.length() == 0) {
293          SAXParser saxParser = XMLParserUtils.getSAXParser(validation.booleanValue(),
294                namespaces.booleanValue());
295          if (saxParser != null) {
296             try {
297                reader = saxParser.getXMLReader();
298             } catch (SAXException e) {
299                LOG.error(Messages.format("conf.configurationError", e));
300             }
301          }
302       }
303 
304       if (reader == null) {
305          if ((readerClassName == null) || (readerClassName.length() == 0)
306                || (readerClassName.equalsIgnoreCase("xerces"))) {
307             readerClassName = "org.apache.xerces.parsers.SAXParser";
308          }
309 
310          reader = XMLParserUtils.instantiateXMLReader(readerClassName);
311       }
312 
313       XMLParserUtils.setFeaturesOnXmlReader(_properties.getString(XMLProperties.PARSER_FEATURES, features),
314             _properties.getString(XMLProperties.PARSER_FEATURES_DISABLED, ""), validation.booleanValue(),
315             namespaces.booleanValue(), reader);
316 
317       return reader;
318 
319    } // -- getXMLReader
320 
321    /**
322     * @see org.castor.xml.InternalContext#getPrimitiveNodeType()
323     */
324    public NodeType getPrimitiveNodeType() {
325 
326       if (_primitiveNodeType != null) {
327          return _primitiveNodeType;
328       }
329 
330       String prop = _properties.getString(XMLProperties.PRIMITIVE_NODE_TYPE, null);
331       if (prop == null) {
332          return null;
333       }
334       _primitiveNodeType = NodeType.getNodeType(prop);
335       return _primitiveNodeType;
336    } // -- getPrimitiveNodeType
337 
338    /**
339     * {@inheritDoc}
340     * 
341     * @see org.castor.xml.InternalContext#getRegExpEvaluator()
342     */
343    public RegExpEvaluator getRegExpEvaluator() {
344       if (_regExpEvaluator != null) {
345          return _regExpEvaluator;
346       }
347 
348       String className = _properties.getString(XMLProperties.REG_EXP_CLASS_NAME, "");
349       if (className.length() == 0) {
350          _regExpEvaluator = null;
351       } else {
352          try {
353             Class<?> regExpEvalClass = Class.forName(className);
354             _regExpEvaluator = (RegExpEvaluator) regExpEvalClass.newInstance();
355          } catch (ClassNotFoundException e) {
356             throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
357          } catch (InstantiationException e) {
358             throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
359          } catch (IllegalAccessException e) {
360             throw new RuntimeException(Messages.format("conf.failedInstantiateRegExp", className, e));
361          }
362       }
363       return _regExpEvaluator;
364    } // -- getRegExpEvaluator
365 
366    /**
367     * @see org.castor.xml.InternalContext#getSerializer()
368     */
369    public Serializer getSerializer() {
370       return XMLParserUtils.getSerializer(_properties);
371    }
372 
373    /**
374     * @see org.castor.xml.InternalContext#getOutputFormat()
375     */
376    public OutputFormat getOutputFormat() {
377       return XMLParserUtils.getOutputFormat(_properties);
378    }
379 
380    /**
381     * Returns the currently configured XMLSerializerFactory instance.
382     * 
383     * @param serializerFactoryName
384     *           the class name of the serializer factory
385     * @return XMLSerializerFactory to use by Castor
386     */
387    protected XMLSerializerFactory getSerializerFactory(final String serializerFactoryName) {
388       return XMLParserUtils.getSerializerFactory(serializerFactoryName);
389    }
390 
391    /**
392     * @see org.castor.xml.InternalContext#getSerializer(java.io.OutputStream)
393     */
394    public DocumentHandler getSerializer(final OutputStream output) throws IOException {
395       Serializer serializer;
396       DocumentHandler docHandler;
397 
398       serializer = getSerializer();
399       serializer.setOutputByteStream(output);
400       docHandler = serializer.asDocumentHandler();
401       if (docHandler == null) {
402          throw new RuntimeException(Messages.format("conf.serializerNotSaxCapable", serializer.getClass()
403                .getName()));
404       }
405       return docHandler;
406    }
407 
408    /**
409     * @see org.castor.xml.InternalContext#getSerializer(java.io.Writer)
410     */
411    public DocumentHandler getSerializer(final Writer output) throws IOException {
412       Serializer serializer;
413       DocumentHandler docHandler;
414 
415       serializer = getSerializer();
416       serializer.setOutputCharStream(output);
417       docHandler = serializer.asDocumentHandler();
418       if (docHandler == null) {
419          throw new RuntimeException(Messages.format("conf.serializerNotSaxCapable", serializer.getClass()
420                .getName()));
421       }
422       return docHandler;
423    }
424 
425    /**
426     * @see org.castor.xml.InternalContext#getXMLClassDescriptorResolver()
427     */
428    public XMLClassDescriptorResolver getXMLClassDescriptorResolver() {
429       return _xmlClassDescriptorResolver;
430    }
431 
432    /**
433     * @see org.castor.xml.InternalContext#getIntrospector()
434     */
435    public Introspector getIntrospector() {
436       return _introspector;
437    }
438 
439    /**
440     * @see org.castor.xml.InternalContext#getResolverStrategy()
441     */
442    public ResolverStrategy getResolverStrategy() {
443       return _resolverStrategy;
444    }
445 
446    /**
447     * @see org.castor.xml.InternalContext#setResolverStrategy(org.exolab.castor.xml.util.ResolverStrategy)
448     */
449    public void setResolverStrategy(final ResolverStrategy resolverStrategy) {
450       _resolverStrategy = resolverStrategy;
451    }
452 
453    /**
454     * @see org.castor.xml.InternalContext#setMappingLoader(org.exolab.castor.mapping.MappingLoader)
455     */
456    public void setMappingLoader(final MappingLoader mappingLoader) {
457       _mappingLoader = mappingLoader;
458    }
459 
460    /**
461     * @see org.castor.xml.InternalContext#getMappingLoader()
462     */
463    public MappingLoader getMappingLoader() {
464       return _mappingLoader;
465    }
466 
467    /**
468     * @see org.castor.xml.InternalContext#setJavaNaming(org.castor.xml.JavaNaming)
469     */
470    public void setJavaNaming(final JavaNaming javaNaming) {
471       _javaNaming = javaNaming;
472    }
473 
474    public void setJavaNaming(final String javaNamingProperty) {
475       if (javaNamingProperty == null || javaNamingProperty.length() == 0) {
476          _javaNaming = new JavaNamingImpl(this);
477       } else {
478          try {
479             Class<JavaNaming> cls = (Class<JavaNaming>) Class.forName(javaNamingProperty);
480             _javaNaming = cls.newInstance();
481          } catch (Exception e) {
482             IllegalArgumentException iae = new IllegalArgumentException("Failed to load JavaNaming: " + e);
483             LOG.warn(iae.getMessage());
484             throw iae;
485          }
486       }
487    }
488 
489    /**
490     * @see org.castor.xml.InternalContext#setXMLNaming(org.castor.xml.XMLNaming)
491     */
492    public void setXMLNaming(final XMLNaming xmlNaming) {
493       _xmlNaming = xmlNaming;
494       // propagate to e.g. Introspector also!!
495       if (_introspector != null) {
496          _introspector.setNaming(_xmlNaming);
497       }
498    }
499 
500    /**
501     * This XMLNaming setter is meant to be used when working in property style
502     * instead of setting an XMLNaming implementation.
503     * 
504     * @param xmlNamingProperty
505     *           to set the XMLNaming property as read from configuration
506     */
507    public void setXMLNaming(final String xmlNamingProperty) {
508       if ((xmlNamingProperty == null) || (xmlNamingProperty.equalsIgnoreCase("lower"))) {
509          setXMLNaming(new DefaultNaming());
510       } else if (xmlNamingProperty.equalsIgnoreCase("mixed")) {
511          DefaultNaming dn = new DefaultNaming();
512          dn.setStyle(DefaultNaming.MIXED_CASE_STYLE);
513          setXMLNaming(dn);
514       } else {
515          try {
516             Class<XMLNaming> cls = (Class<XMLNaming>) Class.forName(xmlNamingProperty);
517             setXMLNaming(cls.newInstance());
518          } catch (Exception e) {
519             IllegalArgumentException iae = new IllegalArgumentException("Failed to load XMLNaming: " + e);
520             LOG.warn(iae.getMessage());
521             throw iae;
522          }
523       }
524       if (_xmlNaming == null) {
525          IllegalArgumentException iae = new IllegalArgumentException(
526                "Failed to correctly set XMLNaming; property was: " + xmlNamingProperty);
527          LOG.warn(iae.getMessage());
528          throw iae;
529       }
530    }
531 
532    /**
533     * @see org.castor.xml.InternalContext#setProperty(java.lang.String, boolean)
534     */
535    public void setProperty(final String propertyName, final boolean value) {
536       this.setPropertyInternal(propertyName, Boolean.valueOf(value));
537    }
538 
539    private void setPropertyInternal(final String propertyName, final Object value) {
540       Object oldValue = this._properties.getObject(propertyName);
541 
542       if (oldValue == null) {
543          if (value != null) {
544             this._properties.put(propertyName, value);
545             this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, value);
546          }
547       } else {
548          if (!oldValue.equals(value)) {
549             this._properties.put(propertyName, value);
550             this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, value);
551          }
552       }
553    }
554 
555    /**
556     * @see org.castor.xml.InternalContext#getBooleanProperty(java.lang.String)
557     */
558    public Boolean getBooleanProperty(final String propertyName) {
559       return _properties.getBoolean(propertyName);
560    }
561 
562    /**
563     * @see org.castor.xml.InternalContext#getStringProperty(java.lang.String)
564     */
565    public String getStringProperty(final String propertyName) {
566       return _properties.getString(propertyName);
567    }
568 
569    /**
570     * @see org.castor.xml.InternalContext#setClassLoader(java.lang.ClassLoader)
571     */
572    public void setClassLoader(final ClassLoader classLoader) {
573       _classLoader = classLoader;
574       if (_xmlClassDescriptorResolver != null) {
575          _xmlClassDescriptorResolver.setClassLoader(classLoader);
576       }
577    }
578 
579    /**
580     * @see org.castor.xml.InternalContext#setXMLClassDescriptorResolver(org.exolab.castor.xml.XMLClassDescriptorResolver)
581     */
582    public void setXMLClassDescriptorResolver(final XMLClassDescriptorResolver xmlClassDescriptorResolver) {
583       _xmlClassDescriptorResolver = xmlClassDescriptorResolver;
584    }
585 
586    /**
587     * @see org.castor.xml.InternalContext#setIntrospector(org.exolab.castor.xml.Introspector)
588     */
589    public void setIntrospector(final Introspector introspector) {
590       _introspector = introspector;
591    }
592 
593    /**
594     * @see org.castor.xml.InternalContext#getClassLoader()
595     */
596    public ClassLoader getClassLoader() {
597       return _classLoader;
598    }
599 
600    /**
601     * @see org.castor.xml.InternalContext#getLenientIdValidation()
602     */
603    public boolean getLenientIdValidation() {
604       Boolean lenientIdValidation = _properties.getBoolean(XMLProperties.LENIENT_ID_VALIDATION);
605       if (lenientIdValidation == null) {
606          String message = "Property lenientIdValidation must not be null";
607          LOG.warn(message);
608          throw new IllegalStateException(message);
609       }
610       return lenientIdValidation.booleanValue();
611    }
612 
613    /**
614     * @see org.castor.xml.InternalContext#getLenientSequenceOrder()
615     */
616    public boolean getLenientSequenceOrder() {
617       Boolean lenientSequenceOrder = _properties.getBoolean(XMLProperties.LENIENT_SEQUENCE_ORDER);
618       if (lenientSequenceOrder == null) {
619          String message = "Property lenientSequenceOrder must not be null";
620          LOG.warn(message);
621          throw new IllegalStateException(message);
622       }
623       return lenientSequenceOrder.booleanValue();
624    }
625 
626    /**
627     * @see org.castor.xml.InternalContext#getLoadPackageMapping()
628     */
629    public Boolean getLoadPackageMapping() {
630       return _properties.getBoolean(XMLProperties.LOAD_PACKAGE_MAPPING);
631    }
632 
633    /**
634     * @see org.castor.xml.InternalContext#setLoadPackageMapping(java.lang.Boolean)
635     */
636    public void setLoadPackageMapping(final Boolean loadPackageMapping) {
637       _properties.put(XMLProperties.LOAD_PACKAGE_MAPPING, loadPackageMapping);
638    }
639 
640    /**
641     * @see org.castor.xml.InternalContext#getUseIntrospector()
642     */
643    public Boolean getUseIntrospector() {
644       return _properties.getBoolean(XMLProperties.USE_INTROSPECTION);
645    }
646 
647    /**
648     * @see org.castor.xml.InternalContext#setUseIntrospector(java.lang.Boolean)
649     */
650    public void setUseIntrospector(final Boolean useIntrospector) {
651       _properties.put(XMLProperties.USE_INTROSPECTION, useIntrospector);
652    }
653 
654    /**
655     * @see org.castor.xml.InternalContext#marshallingValidation()
656     */
657    public boolean marshallingValidation() {
658       Boolean marshallingValidation = _properties.getBoolean(XMLProperties.MARSHALLING_VALIDATION);
659       if (marshallingValidation == null) {
660          String message = "Property marshallingValidation must not be null";
661          LOG.warn(message);
662          throw new IllegalStateException(message);
663       }
664       return marshallingValidation.booleanValue();
665    }
666 
667    /**
668     * @see org.castor.xml.InternalContext#strictElements()
669     */
670    public boolean strictElements() {
671       Boolean strictElements = _properties.getBoolean(XMLProperties.STRICT_ELEMENTS);
672       if (strictElements == null) {
673          String message = "Property strictElements must not be null";
674          LOG.warn(message);
675          throw new IllegalStateException(message);
676       }
677       return strictElements.booleanValue();
678    }
679 
680    public void addPropertyChangeListener(PropertyChangeListener listener) {
681       this.propertyChangeSupport.addPropertyChangeListener(listener);
682    }
683 
684    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
685       this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
686    }
687 
688    public void removePropertyChangeListener(PropertyChangeListener listener) {
689       this.propertyChangeSupport.removePropertyChangeListener(listener);
690    }
691 
692    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
693       this.propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
694    }
695 }