001    package org.apache.commons.digester3.binder;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.commons.digester3.FactoryCreateRule;
023    import org.apache.commons.digester3.ObjectCreationFactory;
024    
025    /**
026     * Builder chained when invoking {@link LinkedRuleBuilder#factoryCreate()}.
027     *
028     * @since 3.0
029     */
030    public final class FactoryCreateBuilder
031        extends AbstractBackToLinkedRuleBuilder<FactoryCreateRule>
032    {
033    
034        private final ClassLoader classLoader;
035    
036        private Class<? extends ObjectCreationFactory<?>> type;
037    
038        private String attributeName;
039    
040        private boolean ignoreCreateExceptions;
041    
042        private ObjectCreationFactory<?> creationFactory;
043    
044        FactoryCreateBuilder( String keyPattern, String namespaceURI, RulesBinder mainBinder,
045                              LinkedRuleBuilder mainBuilder, ClassLoader classLoader )
046        {
047            super( keyPattern, namespaceURI, mainBinder, mainBuilder );
048            this.classLoader = classLoader;
049        }
050    
051        /**
052         * Construct a factory create rule that will use the specified class name to create an {@link ObjectCreationFactory}
053         * which will then be used to create an object and push it on the stack.
054         *
055         * @param className Java class name of the object creation factory class
056         * @return this builder instance
057         */
058        @SuppressWarnings( "unchecked" ) // if class not assignable, will be notified via exception
059        public FactoryCreateBuilder ofType( String className )
060        {
061            if ( className == null )
062            {
063                reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" );
064            }
065    
066            try
067            {
068                Class<?> type = this.classLoader.loadClass( className );
069                if ( !ObjectCreationFactory.class.isAssignableFrom( type ) )
070                {
071                    reportError( "factoryCreate().ofType( String )", "NULL Java type not allowed" );
072                    return this;
073                }
074    
075                this.type = (Class<? extends ObjectCreationFactory<?>>) type;
076            }
077            catch ( ClassNotFoundException e )
078            {
079                reportError( "factoryCreate().ofType( String )", String.format( "class '%s' cannot be load", className ) );
080            }
081    
082            return this;
083        }
084    
085        /**
086         * Construct a factory create rule that will use the specified class to create an {@link ObjectCreationFactory}
087         * which will then be used to create an object and push it on the stack.
088         *
089         * @param type Java class of the object creation factory class
090         * @return this builder instance
091         */
092        public FactoryCreateBuilder ofType( Class<? extends ObjectCreationFactory<?>> type )
093        {
094            if ( type == null )
095            {
096                reportError( "factoryCreate().ofType( Class<? extends ObjectCreationFactory<?>> )",
097                                  "NULL Java type not allowed" );
098            }
099    
100            this.type = type;
101    
102            return this;
103        }
104    
105        /**
106         * Construct a factory create rule using the given, already instantiated, {@link ObjectCreationFactory}.
107         *
108         * @param <T> the type of created object by the given factory
109         * @param creationFactory called on to create the object
110         * @return this builder instance
111         */
112        public <T> FactoryCreateBuilder usingFactory( /* @Nullable */ObjectCreationFactory<T> creationFactory )
113        {
114            this.creationFactory = creationFactory;
115            return this;
116        }
117    
118        /**
119         * Allows specify the attribute containing an override class name if it is present.
120         *
121         * @param attributeName The attribute containing an override class name if it is present
122         * @return this builder instance
123         */
124        public FactoryCreateBuilder overriddenByAttribute( /* @Nullable */String attributeName )
125        {
126            this.attributeName = attributeName;
127            return this;
128        }
129    
130        /**
131         * Exceptions thrown by the object creation factory will be ignored or not.
132         *
133         * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored
134         * @return this builder instance
135         */
136        public FactoryCreateBuilder ignoreCreateExceptions( boolean ignoreCreateExceptions )
137        {
138            this.ignoreCreateExceptions = ignoreCreateExceptions;
139            return this;
140        }
141    
142        /**
143         * {@inheritDoc}
144         */
145        @Override
146        protected FactoryCreateRule createRule()
147        {
148            if ( type == null && attributeName == null && creationFactory == null )
149            {
150                reportError( "factoryCreate()",
151                             "at least one between 'className', 'attributeName' or 'creationFactory' has to be specified" );
152            }
153    
154            if ( type != null || attributeName != null )
155            {
156                return new FactoryCreateRule( type, attributeName, ignoreCreateExceptions );
157            }
158    
159            return new FactoryCreateRule( creationFactory, ignoreCreateExceptions );
160        }
161    
162    }