001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    
018    package org.apache.commons.betwixt;
019    
020    import org.apache.commons.betwixt.strategy.AttributeSuppressionStrategy;
021    import org.apache.commons.betwixt.strategy.ClassNormalizer;
022    import org.apache.commons.betwixt.strategy.CollectiveTypeStrategy;
023    import org.apache.commons.betwixt.strategy.DefaultNameMapper;
024    import org.apache.commons.betwixt.strategy.DefaultPluralStemmer;
025    import org.apache.commons.betwixt.strategy.ElementSuppressionStrategy;
026    import org.apache.commons.betwixt.strategy.MappingDerivationStrategy;
027    import org.apache.commons.betwixt.strategy.NameMapper;
028    import org.apache.commons.betwixt.strategy.NamespacePrefixMapper;
029    import org.apache.commons.betwixt.strategy.PluralStemmer;
030    import org.apache.commons.betwixt.strategy.PropertySuppressionStrategy;
031    import org.apache.commons.betwixt.strategy.SimpleTypeMapper;
032    import org.apache.commons.betwixt.strategy.StandardSimpleTypeMapper;
033    import org.apache.commons.betwixt.strategy.TypeBindingStrategy;
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    
037    /**
038     * <p>Stores introspection phase binding configuration.</p>
039     * <p>
040     * There are two phase in Betwixt's processing. 
041     * The first phase is the introspection of the bean. 
042     * Strutural configuration settings effect this phase. 
043     * The second phase comes when Betwixt dynamically uses reflection 
044     * to execute the mapping. 
045     * This object stores configuration settings pertaining to the first phase.
046     * </p>
047     * <p>
048     * These common settings have been collected into one class so that they can  
049     * be more easily shared not only between the objects that execute the introspection
050     * but also (by a user) between different <code>XMLIntrospector</code>s.
051     * </p>
052     * @author <a href='http://commons.apache.org/'>Apache Commons Team</a>
053     * @version $Revision: 561314 $
054     */
055    public class IntrospectionConfiguration {
056    
057        /** should attributes or elements be used for primitive types */
058        private boolean attributesForPrimitives = false;
059        
060        /** should we wrap collections in an extra element? */
061        private boolean wrapCollectionsInElement = true;
062    
063        /** Should the existing bean info search path for java.reflect.Introspector be used? */
064        private boolean useBeanInfoSearchPath = false;
065        
066        /** Should existing BeanInfo classes be used at all for java.reflect.Introspector */
067        private boolean ignoreAllBeanInfo = false;
068    
069        // pluggable strategies        
070        /** The strategy used to detect matching singular and plural properties */
071        private PluralStemmer pluralStemmer;
072        
073        /** The strategy used to convert bean type names into element names */
074        private NameMapper elementNameMapper;
075    
076        /** Strategy normalizes the Class of the Object before introspection */
077        private ClassNormalizer classNormalizer = new ClassNormalizer(); 
078        
079        /** Log for introspection messages */
080        private Log introspectionLog = LogFactory.getLog(XMLIntrospector.class);
081    
082        /**
083         * The strategy used to convert bean type names into attribute names
084         * It will default to the normal nameMapper.
085         */
086        private NameMapper attributeNameMapper;
087    
088        /** Prefix naming strategy */
089        private NamespacePrefixMapper prefixMapper = new NamespacePrefixMapper();
090        /** Mapping strategy for simple types */
091        private SimpleTypeMapper simpleTypeMapper = new StandardSimpleTypeMapper();
092        /** Binding strategy for Java type */
093        private TypeBindingStrategy typeBindingStrategy = TypeBindingStrategy.DEFAULT;
094        /** Strategy used for determining which types are collective */
095        private CollectiveTypeStrategy collectiveTypeStrategy = CollectiveTypeStrategy.DEFAULT;
096    
097        /** Strategy for suppressing attributes */
098        private AttributeSuppressionStrategy attributeSuppressionStrategy = AttributeSuppressionStrategy.DEFAULT;   
099        /** Strategy for suppressing elements */
100        private ElementSuppressionStrategy elementSuppressionStrategy = ElementSuppressionStrategy.DEFAULT;
101    
102        
103        /** 
104         * Strategy used to determine whether the bind or introspection time type is to be used to  
105         * determine the mapping.
106         */
107            private MappingDerivationStrategy mappingDerivationStrategy = MappingDerivationStrategy.DEFAULT;
108        
109            /**
110             * Strategy used to determine which properties should be ignored
111             */
112            private PropertySuppressionStrategy propertySuppressionStrategy = PropertySuppressionStrategy.DEFAULT;
113            
114            /**
115             * Should the introspector use the context classloader. Defaults to true.
116             */
117            private boolean useContextClassLoader = true;
118            
119        /**
120          * Gets the <code>ClassNormalizer</code> strategy.
121          * This is used to determine the Class to be introspected
122          * (the normalized Class). 
123          *
124          * @return the <code>ClassNormalizer</code> used to determine the Class to be introspected
125          * for a given Object.
126          */
127        public ClassNormalizer getClassNormalizer() {
128            return classNormalizer;
129        }
130        
131        /**
132          * Sets the <code>ClassNormalizer</code> strategy.
133          * This is used to determine the Class to be introspected
134          * (the normalized Class). 
135          *
136          * @param classNormalizer the <code>ClassNormalizer</code> to be used to determine 
137          * the Class to be introspected for a given Object.
138          */    
139        public void setClassNormalizer(ClassNormalizer classNormalizer) {
140            this.classNormalizer = classNormalizer;
141        }
142    
143        /** 
144          * Should attributes (or elements) be used for primitive types.
145          * @return true if primitive types will be mapped to attributes in the introspection
146          */
147        public boolean isAttributesForPrimitives() {
148            return attributesForPrimitives;
149        }
150    
151        /** 
152          * Set whether attributes (or elements) should be used for primitive types. 
153          * @param attributesForPrimitives pass trus to map primitives to attributes,
154          *        pass false to map primitives to elements
155          */
156        public void setAttributesForPrimitives(boolean attributesForPrimitives) {
157            this.attributesForPrimitives = attributesForPrimitives;
158        }
159        
160        /**
161         * Should collections be wrapped in an extra element?
162         * 
163         * @return whether we should we wrap collections in an extra element? 
164         */
165        public boolean isWrapCollectionsInElement() {
166            return wrapCollectionsInElement;
167        }
168    
169        /** 
170         * Sets whether we should we wrap collections in an extra element.
171         *
172         * @param wrapCollectionsInElement pass true if collections should be wrapped in a
173         *        parent element
174         */
175        public void setWrapCollectionsInElement(boolean wrapCollectionsInElement) {
176            this.wrapCollectionsInElement = wrapCollectionsInElement;
177        }    
178    
179        /** 
180         * Get singular and plural matching strategy.
181         *
182         * @return the strategy used to detect matching singular and plural properties 
183         */
184        public PluralStemmer getPluralStemmer() {
185            if ( pluralStemmer == null ) {
186                pluralStemmer = createPluralStemmer();
187            }
188            return pluralStemmer;
189        }
190        
191        /** 
192         * Sets the strategy used to detect matching singular and plural properties 
193         *
194         * @param pluralStemmer the PluralStemmer used to match singular and plural
195         */
196        public void setPluralStemmer(PluralStemmer pluralStemmer) {
197            this.pluralStemmer = pluralStemmer;
198        }
199        
200        /**
201         * Gets the name mapping strategy used to convert bean names into elements.
202         *
203         * @return the strategy used to convert bean type names into element 
204         * names. If no element mapper is currently defined then a default one is created.
205         */
206        public NameMapper getElementNameMapper() {
207            if ( elementNameMapper == null ) {
208                elementNameMapper = createNameMapper();
209             }
210            return elementNameMapper;
211        }
212         
213        /**
214         * Sets the strategy used to convert bean type names into element names
215         * @param nameMapper the NameMapper to use for the conversion
216         */
217        public void setElementNameMapper(NameMapper nameMapper) {
218            this.elementNameMapper = nameMapper;
219        }
220        
221        /**
222         * Gets the name mapping strategy used to convert bean names into attributes.
223         *
224         * @return the strategy used to convert bean type names into attribute
225         * names. If no attributeNamemapper is known, it will default to the ElementNameMapper
226         */
227        public NameMapper getAttributeNameMapper() {
228            if (attributeNameMapper == null) {
229                attributeNameMapper = createNameMapper();
230            }
231            return attributeNameMapper;
232         }
233    
234    
235        /**
236         * Sets the strategy used to convert bean type names into attribute names
237         * @param nameMapper the NameMapper to use for the convertion
238         */
239        public void setAttributeNameMapper(NameMapper nameMapper) {
240            this.attributeNameMapper = nameMapper;
241        }
242        
243        /**
244         * <p>Should the original <code>java.reflect.Introspector</code> bean info search path be used?</p>
245         * <p>
246         * Default is false.
247         * </p>
248         * 
249         * @return boolean if the beanInfoSearchPath should be used.
250         */
251        public boolean useBeanInfoSearchPath() {
252            return useBeanInfoSearchPath;
253        }
254    
255        /**
256         * Specifies if you want to use the beanInfoSearchPath 
257         * @see java.beans.Introspector for more details
258         * @param useBeanInfoSearchPath 
259         */
260        public void setUseBeanInfoSearchPath(boolean useBeanInfoSearchPath) {
261            this.useBeanInfoSearchPath = useBeanInfoSearchPath;
262        }
263        
264        /**
265         * <p>Should existing BeanInfo classes be ignored by <code>java.reflect.Introspector</code>.</p>
266         * <p>
267         * Default is false.
268         * </p>
269         * 
270         * @return boolean if the BeanInfo classes should be used.
271         */
272        public boolean ignoreAllBeanInfo() {
273            return ignoreAllBeanInfo;
274        }
275        
276        /**
277         * Specifies if you want to ignore existing BeanInfo classes at all for introspection
278         * @see java.beans.Introspector for more details
279         * @param ignoreAllBeanInfo set to true to ignore all BeanInfo classes
280         * @since 0.8
281         */
282        public void setIgnoreAllBeanInfo(boolean ignoreAllBeanInfo) {
283            this.ignoreAllBeanInfo = ignoreAllBeanInfo;
284        }
285        
286        
287        /** 
288         * A Factory method to lazily create a new strategy 
289         * to detect matching singular and plural properties.
290         *
291         * @return new defualt PluralStemmer implementation
292         */
293        protected PluralStemmer createPluralStemmer() {
294            return new DefaultPluralStemmer();
295        }
296        
297        /** 
298         * A Factory method to lazily create a strategy 
299         * used to convert bean type names into element names.
300         *
301         * @return new default NameMapper implementation
302         */
303        protected NameMapper createNameMapper() {
304            return new DefaultNameMapper();
305        }
306        
307        /**
308         * Gets the common Log used for introspection.
309         * It is more convenient to use a single Log
310         * that can be easily configured.
311         * @return Log, not null
312         */
313        public Log getIntrospectionLog() {
314            return introspectionLog;
315        }
316    
317        /**
318         * Sets the common Log used by introspection.
319         * It is more convenient to use a single Log
320         * that can be easily configured.
321         * @param log Log, not null
322         */
323        public void setIntrospectionLog(Log log) {
324            introspectionLog = log;
325        }
326    
327        
328        /**
329         * Gets the <code>NamespacePrefixMapper</code> used to convert namespace URIs 
330         * into prefixes.
331         * @return NamespacePrefixMapper, not null
332         */
333        public NamespacePrefixMapper getPrefixMapper() {
334            return prefixMapper;
335        }
336    
337        /**
338         * Sets the <code>NamespacePrefixMapper</code> used to convert namespave URIs
339         * into prefixes.
340         * @param mapper NamespacePrefixMapper, not null
341         */
342        public void setPrefixMapper(NamespacePrefixMapper mapper) {
343            prefixMapper = mapper;
344        }
345        
346        
347        /**
348         * Gets the simple type binding strategy.
349         * @return SimpleTypeMapper, not null
350         */
351        public SimpleTypeMapper getSimpleTypeMapper() {
352            return simpleTypeMapper;
353        }
354    
355        /**
356         * Sets the simple type binding strategy.
357         * @param mapper SimpleTypeMapper, not null
358         */
359        public void setSimpleTypeMapper(SimpleTypeMapper mapper) {
360            simpleTypeMapper = mapper;
361        }
362    
363        /**
364         * Gets the <code>TypeBindingStrategy</code> to be used
365         * to determine the binding for Java types.
366         * @return the <code>TypeBindingStrategy</code> to be used, 
367         * not null
368         */
369        public TypeBindingStrategy getTypeBindingStrategy() {
370            return typeBindingStrategy;
371        }
372        
373        /**
374         * Sets the <code>TypeBindingStrategy</code> to be used
375         * to determine the binding for Java types.
376         * @param typeBindingStrategy the <code>TypeBindingStrategy</code> to be used,
377         * not null
378         */
379        public void setTypeBindingStrategy(TypeBindingStrategy typeBindingStrategy) {
380            this.typeBindingStrategy = typeBindingStrategy;
381        }
382        
383        
384        /**
385         * Gets the <code>MappingDerivationStrategy</code>
386         * used to determine whether the bind or introspection time
387         * type should determine the mapping.
388         * @since 0.7
389         * @return <code>MappingDerivationStrategy</code>, not null
390         */
391        public MappingDerivationStrategy getMappingDerivationStrategy() {
392            return mappingDerivationStrategy;
393        }
394        /**
395         * Sets the <code>MappingDerivationStrategy</code>
396         * used to determine whether the bind or introspection time
397         * type should determine the mapping.
398         * @since 0.7
399         * @param mappingDerivationStrategy <code>MappingDerivationStrategy</code>, not null
400         */
401        public void setMappingDerivationStrategy(
402                MappingDerivationStrategy mappingDerivationStrategy) {
403            this.mappingDerivationStrategy = mappingDerivationStrategy;
404        }
405    
406        /**
407         * Gets the strategy which determines the properties to be ignored.
408         * @since 0.7
409         * @return the <code>PropertySuppressionStrategy</code> to be used for introspection, not null
410         */
411        public PropertySuppressionStrategy getPropertySuppressionStrategy() {
412            return propertySuppressionStrategy;
413        }
414        
415        /**
416         * Sets the strategy which determines the properties to be ignored.
417         * @since 0.7
418         * @param propertySuppressionStrategy the <code>PropertySuppressionStrategy</code> to be used for introspection, not null
419         */
420        public void setPropertySuppressionStrategy(
421                PropertySuppressionStrategy propertySuppressionStrategy) {
422            this.propertySuppressionStrategy = propertySuppressionStrategy;
423        }
424        
425        /**
426         * Gets the strategy used to determine which types are collective.
427         * @return <code>CollectiveTypeStrategy</code>, not null
428         * @since 0.8
429         */
430        public CollectiveTypeStrategy getCollectiveTypeStrategy() {
431            return collectiveTypeStrategy;
432        }
433    
434        /**
435         * Sets the strategy used to determine which types are collective.
436         * @param collectiveTypeStrategy <code>CollectiveTypeStrategy</code>, not null
437         * @since 0.8
438         */
439        public void setCollectiveTypeStrategy(
440                CollectiveTypeStrategy collectiveTypeStrategy) {
441            this.collectiveTypeStrategy = collectiveTypeStrategy;
442        }
443    
444        /** 
445         * Is this a loop type class?
446         * @since 0.7
447         * @param type is this <code>Class</code> a loop type?
448         * @return true if the type is a loop type, or if type is null 
449         */
450        public boolean isLoopType(Class type) {
451            return getCollectiveTypeStrategy().isCollective(type);
452        }
453    
454    
455        /**
456         * Returns the <code>AttributeSuppressionStrategy</code>. 
457         * This is used to suppress attributes, e.g. for versioning.
458         * 
459         * @since 0.8
460         * @return the strategy
461         */
462        public AttributeSuppressionStrategy getAttributeSuppressionStrategy() {
463            return attributeSuppressionStrategy;
464        }
465    
466        /**
467         * Sets the <code>AttributeSuppressionStrategy</code>. 
468         * This is used to suppress attributes, e.g. for versioning.
469         * 
470         * @since 0.8
471         * @param attributeSuppressionStrategy the strategy 
472         */
473        public void setAttributeSuppressionStrategy(
474                AttributeSuppressionStrategy attributeSuppressionStrategy) {
475            this.attributeSuppressionStrategy = attributeSuppressionStrategy;
476        }
477    
478        /**
479         * Returns the <code>ElementSuppressionStrategy</code>. 
480         * This is used to suppress elements, e.g. for versioning.
481         * 
482         * @since 0.8
483         * @return the strategy
484         */
485        public ElementSuppressionStrategy getElementSuppressionStrategy() {
486            return elementSuppressionStrategy;
487        }
488    
489        /**
490         * Sets the <code>ElementSuppressionStrategy</code>. 
491         * This is used to suppress elements, e.g. for versioning.
492         * 
493         * @since 0.8
494         * @param elementSuppressionStrategy the strategy 
495         */
496        public void setElementSuppressionStrategy(
497                ElementSuppressionStrategy elementSuppressionStrategy) {
498            this.elementSuppressionStrategy = elementSuppressionStrategy;
499        }
500    
501        /**
502         * Should be context classloader be used when loading classes?
503         * @return <code>true</code> if the context classloader is to be used during introspection, 
504         * <code>false</code> otherwise.
505         */
506        public boolean isUseContextClassLoader() {
507            return useContextClassLoader;
508        }
509    
510        /**
511         * <p>Specify whether the context classloader should be used to load classes during introspection;
512         * the default value is true.</p>
513         * <p>
514         * When running code that is not in a container (ie where the context classloader is the same
515         * as the system classloader), this setting has no effect. When running code in containers that
516         * do define a context classloader for loaded "components" (eg webapps), a true value will allow
517         * classes in the loaded "component" to be accessable even when Betwixt is deployed via a
518         * "higher level" classloader.
519         * </p>
520         * <p>
521         * If code is running in a container that uses a context classloader in unusual ways then it
522         * may be necessary to set this value to false. In this case, classes are always loaded using the
523         * same classloader that loaded the betwixt library.
524         * </p>
525         */
526        public void setUseContextClassLoader(boolean useContextClassLoader) {
527            this.useContextClassLoader = useContextClassLoader;
528        }
529    }