001package 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
022import static java.lang.String.format;
023
024import java.util.Arrays;
025
026import org.apache.commons.digester3.ObjectCreateRule;
027
028/**
029 * Builder chained when invoking {@link LinkedRuleBuilder#createObject()}.
030 *
031 * @since 3.0
032 */
033public 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}