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 static java.lang.String.format;
023    
024    import java.util.Arrays;
025    
026    import org.apache.commons.digester3.ObjectCreateRule;
027    
028    /**
029     * Builder chained when invoking {@link LinkedRuleBuilder#createObject()}.
030     *
031     * @since 3.0
032     */
033    public final class ObjectCreateBuilder
034        extends AbstractBackToLinkedRuleBuilder<ObjectCreateRule>
035    {
036    
037        private final ClassLoader classLoader;
038    
039        private Class<?> type;
040    
041        private String attributeName;
042    
043        /**
044         * The constructor argument types
045         *
046         * @since 3.2
047         */
048        private Class<?>[] constructorArgumentsType;
049    
050        /**
051         * Default constructor arguments.
052         *
053         * @since 3.2
054         */
055        private Object[] defaultConstructorArguments;
056    
057        ObjectCreateBuilder( String keyPattern, String namespaceURI, RulesBinder mainBinder, LinkedRuleBuilder mainBuilder,
058                             ClassLoader classLoader )
059        {
060            super( keyPattern, namespaceURI, mainBinder, mainBuilder );
061            this.classLoader = classLoader;
062        }
063    
064        /**
065         * Construct an object with the specified class name.
066         *
067         * @param className Java class name of the object to be created
068         * @return this builder instance
069         */
070        public ObjectCreateBuilder ofType( String className )
071        {
072            if ( className == null )
073            {
074                reportError( "createObject().ofType( String )", "NULL Java type not allowed" );
075                return this;
076            }
077    
078            try
079            {
080                return ofType( this.classLoader.loadClass( className ) );
081            }
082            catch ( ClassNotFoundException e )
083            {
084                reportError( "createObject().ofType( String )", String.format( "class '%s' cannot be load", className ) );
085                return this;
086            }
087        }
088    
089        /**
090         * Construct an object with the specified class.
091         *
092         * @param <T> any java type
093         * @param type Java class of the object to be created
094         * @return this builder instance
095         */
096        public <T> ObjectCreateBuilder ofType( Class<T> type )
097        {
098            if ( type == null )
099            {
100                reportError( "createObject().ofType( Class<?> )", "NULL Java type not allowed" );
101                return this;
102            }
103    
104            this.type = type;
105    
106            return this;
107        }
108    
109        /**
110         * Allows specify the attribute containing an override class name if it is present.
111         *
112         * @param attributeName The attribute containing an override class name if it is present
113         * @return this builder instance
114         */
115        public ObjectCreateBuilder ofTypeSpecifiedByAttribute( /* @Nullable */String attributeName )
116        {
117            this.attributeName = attributeName;
118            return this;
119        }
120    
121        /**
122         * Allows users to specify constructor argument type names.
123         *
124         * @param paramTypeNames the constructor argument type names
125         * @return this builder instance
126         * @since 3.2
127         */
128        public ObjectCreateBuilder usingConstructor( String...paramTypeNames )
129        {
130            if ( paramTypeNames == null )
131            {
132                reportError( "createObject().usingConstructor( String[] )", "NULL parametersTypes not allowed" );
133                return this;
134            }
135    
136            Class<?>[] paramTypes = new Class<?>[paramTypeNames.length];
137            for ( int i = 0; i < paramTypeNames.length; i++ )
138            {
139                try
140                {
141                    paramTypes[i] = classLoader.loadClass( paramTypeNames[i] );
142                }
143                catch ( ClassNotFoundException e )
144                {
145                    this.reportError( format( "createObject().usingConstructor( %s )",
146                                              Arrays.toString( paramTypeNames ) ),
147                                      format( "class '%s' cannot be loaded", paramTypeNames[i] ) );
148                }
149            }
150    
151            return usingConstructor( paramTypes );
152        }
153    
154        /**
155         * Allows users to specify constructor argument types.
156         *
157         * @param constructorArgumentTypes the constructor argument types
158         * @return this builder instance
159         * @since 3.2
160         */
161        public ObjectCreateBuilder usingConstructor( Class<?>... constructorArgumentTypes )
162        {
163            if ( constructorArgumentTypes == null )
164            {
165                reportError( "createObject().usingConstructor( Class<?>[] )",
166                             "NULL constructorArgumentTypes not allowed" );
167                return this;
168            }
169    
170            this.constructorArgumentsType = constructorArgumentTypes;
171    
172            return this;
173        }
174    
175        /**
176         * Allows users to specify default constructor arguments.
177         *
178         * @param defaultConstructorArguments the default constructor arguments.
179         * @return this builder instance
180         * @since 3.2
181         */
182        public ObjectCreateBuilder usingDefaultConstructorArguments( Object... defaultConstructorArguments )
183        {
184            if ( defaultConstructorArguments == null )
185            {
186                reportError( "createObject().usingDefaultConstructorArguments( Object[] )",
187                             "NULL defaultConstructorArguments not allowed" );
188                return this;
189            }
190    
191            this.defaultConstructorArguments = defaultConstructorArguments;
192    
193            return this;
194    
195        }
196    
197        /**
198         * {@inheritDoc}
199         */
200        @Override
201        protected ObjectCreateRule createRule()
202        {
203            ObjectCreateRule objectCreateRule = new ObjectCreateRule( attributeName, type );
204    
205            if ( constructorArgumentsType != null )
206            {
207                objectCreateRule.setConstructorArgumentTypes( constructorArgumentsType );
208            }
209            if ( defaultConstructorArguments != null )
210            {
211                objectCreateRule.setDefaultConstructorArguments( defaultConstructorArguments );
212            }
213    
214            return objectCreateRule;
215        }
216    
217    }