001/*
002 *   Copyright (C) Christian Schulte <cs@schulte.it>, 2011-293
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: KeyValueType.java 5067 2015-07-06 07:31:18Z schulte $
029 *
030 */
031package org.jomc.mojo;
032
033import java.lang.reflect.InvocationTargetException;
034import java.lang.reflect.Method;
035import java.lang.reflect.Modifier;
036import org.apache.commons.lang.builder.ToStringBuilder;
037import org.jomc.modlet.ModelContext;
038import org.jomc.modlet.ModelException;
039
040/**
041 * Datatype holding a {@code key}, {@code value} and {@code type} property.
042 *
043 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
044 * @version $JOMC: KeyValueType.java 5067 2015-07-06 07:31:18Z schulte $
045 * @since 1.2
046 */
047public class KeyValueType implements Cloneable
048{
049
050    /**
051     * The key of the type.
052     */
053    private String key;
054
055    /**
056     * The value of the type.
057     */
058    private String value;
059
060    /**
061     * The name of the class of the type of {@code value}.
062     */
063    private String type;
064
065    /**
066     * Creates a new {@code KeyValueType} instance.
067     */
068    public KeyValueType()
069    {
070        super();
071    }
072
073    /**
074     * Gets the value of the {@code key} property.
075     *
076     * @return The value of the {@code key} property.
077     *
078     * @see #setKey(java.lang.String)
079     */
080    public final String getKey()
081    {
082        return this.key;
083    }
084
085    /**
086     * Sets the value of the {@code key} property.
087     *
088     * @param k The new value of the {@code key} property.
089     *
090     * @see #getKey()
091     */
092    public final void setKey( final String k )
093    {
094        this.key = k;
095    }
096
097    /**
098     * Gets the value of the {@code value} property.
099     *
100     * @return The value of the {@code value} property or {@code null}.
101     *
102     * @see #setValue(java.lang.String)
103     */
104    public final String getValue()
105    {
106        return this.value;
107    }
108
109    /**
110     * Sets the value of the {@code value} property.
111     *
112     * @param v The new value of the {@code value} property or {@code null}.
113     *
114     * @see #getValue()
115     */
116    public final void setValue( final String v )
117    {
118        this.value = v;
119    }
120
121    /**
122     * Gets the value of the {@code type} property.
123     *
124     * @return The value of the {@code type} property or {@code null}.
125     *
126     * @see #setType(java.lang.String)
127     */
128    public final String getType()
129    {
130        return this.type;
131    }
132
133    /**
134     * Sets the value of the {@code type} property.
135     *
136     * @param t The new value of the {@code type} property or {@code null}.
137     *
138     * @see #getType()
139     */
140    public final void setType( final String t )
141    {
142        this.type = t;
143    }
144
145    /**
146     * Gets the object of the instance.
147     *
148     * @return The object of the instance or {@code null}.
149     *
150     * @throws InstantiationException if getting the object of the instance fails.
151     *
152     * @see #getType()
153     * @see #getValue()
154     * @deprecated As of JOMC 1.8, replaced by method {@link #getObject(org.jomc.modlet.ModelContext)}. This method
155     * will be removed in JOMC 2.0.
156     */
157    @Deprecated
158    public Object getObject() throws InstantiationException // JDK: As of JDK 7, "throws ReflectiveOperationException".
159    {
160        Class<?> javaClass = null;
161        Object o = this.getValue();
162
163        try
164        {
165            if ( o != null )
166            {
167                if ( this.getType() != null && !String.class.getName().equals( this.getType() ) )
168                {
169                    javaClass = Class.forName( this.getType() );
170
171                    try
172                    {
173                        o = javaClass.getConstructor( String.class ).newInstance( o );
174                    }
175                    catch ( final NoSuchMethodException e )
176                    {
177                        final Method valueOf = javaClass.getMethod( "valueOf", String.class );
178
179                        if ( Modifier.isStatic( valueOf.getModifiers() )
180                                 && valueOf.getReturnType().equals( javaClass ) )
181                        {
182                            o = valueOf.invoke( null, o );
183                        }
184                        else
185                        {
186                            throw (InstantiationException) new InstantiationException(
187                                Messages.getMessage( "noSuchMethodCreatingObject", this.getType(), this.getValue(),
188                                                     javaClass.getSimpleName() ) ).initCause( e );
189
190                        }
191                    }
192                }
193            }
194            else if ( this.getType() != null )
195            {
196                o = Class.forName( this.getType() ).newInstance();
197            }
198
199            return o;
200        }
201        catch ( final ClassNotFoundException e )
202        {
203            throw (InstantiationException) new InstantiationException(
204                Messages.getMessage( "classNotFound", this.getType() ) ).initCause( e );
205
206        }
207        catch ( final NoSuchMethodException e )
208        {
209            throw (InstantiationException) new InstantiationException(
210                Messages.getMessage( "noSuchMethodCreatingObject", this.getType(), this.getValue(),
211                                     javaClass.getSimpleName() ) ).initCause( e );
212
213        }
214        catch ( final IllegalAccessException e )
215        {
216            throw (InstantiationException) new InstantiationException(
217                Messages.getMessage( "failedCreatingObject", this.getType() ) ).initCause( e );
218
219        }
220        catch ( final InvocationTargetException e )
221        {
222            throw (InstantiationException) new InstantiationException(
223                Messages.getMessage( "failedCreatingObject", this.getType() ) ).initCause( e );
224
225        }
226    }
227
228    /**
229     * Gets the object of the instance.
230     *
231     * @param modelContext The context to use for getting the object of the instance.
232     *
233     * @return The object of the instance or {@code null}.
234     *
235     * @throws NullPointerException if {@code modelContext} is {@code null}.
236     * @throws InstantiationException if getting the object of the instance fails.
237     *
238     * @see #getType()
239     * @see #getValue()
240     *
241     * @since 1.8
242     */
243    public Object getObject( final ModelContext modelContext ) throws InstantiationException // JDK: As of JDK 7, "throws ReflectiveOperationException".
244    {
245        if ( modelContext == null )
246        {
247            throw new NullPointerException( "modelContext" );
248        }
249
250        Class<?> javaClass = null;
251        Object o = this.getValue();
252
253        try
254        {
255            if ( o != null )
256            {
257                if ( this.getType() != null && !String.class.getName().equals( this.getType() ) )
258                {
259                    javaClass = modelContext.findClass( this.getType() );
260
261                    if ( javaClass == null )
262                    {
263                        throw new InstantiationException( Messages.getMessage( "classNotFound", this.getType() ) );
264                    }
265
266                    try
267                    {
268                        o = javaClass.getConstructor( String.class ).newInstance( o );
269                    }
270                    catch ( final NoSuchMethodException e )
271                    {
272                        final Method valueOf = javaClass.getMethod( "valueOf", String.class );
273
274                        if ( Modifier.isStatic( valueOf.getModifiers() )
275                                 && valueOf.getReturnType().equals( javaClass ) )
276                        {
277                            o = valueOf.invoke( null, o );
278                        }
279                        else
280                        {
281                            throw (InstantiationException) new InstantiationException(
282                                Messages.getMessage( "noSuchMethodCreatingObject", this.getType(), this.getValue(),
283                                                     javaClass.getSimpleName() ) ).initCause( e );
284
285                        }
286                    }
287                }
288            }
289            else if ( this.getType() != null )
290            {
291                javaClass = modelContext.findClass( this.getType() );
292
293                if ( javaClass == null )
294                {
295                    throw new InstantiationException( Messages.getMessage( "classNotFound", this.getType() ) );
296                }
297
298                o = javaClass.newInstance();
299            }
300
301            return o;
302        }
303        catch ( final ModelException e )
304        {
305            throw (InstantiationException) new InstantiationException(
306                Messages.getMessage( "failedSearchingClass", this.getType() ) ).initCause( e );
307
308        }
309        catch ( final NoSuchMethodException e )
310        {
311            throw (InstantiationException) new InstantiationException(
312                Messages.getMessage( "noSuchMethodCreatingObject", this.getType(), this.getValue(),
313                                     javaClass.getSimpleName() ) ).initCause( e );
314
315        }
316        catch ( final IllegalAccessException e )
317        {
318            throw (InstantiationException) new InstantiationException(
319                Messages.getMessage( "failedCreatingObject", this.getType() ) ).initCause( e );
320
321        }
322        catch ( final InvocationTargetException e )
323        {
324            throw (InstantiationException) new InstantiationException(
325                Messages.getMessage( "failedCreatingObject", this.getType() ) ).initCause( e );
326
327        }
328    }
329
330    /**
331     * Creates and returns a copy of this object.
332     *
333     * @return A copy of this object.
334     */
335    @Override
336    public KeyValueType clone()
337    {
338        try
339        {
340            return (KeyValueType) super.clone();
341        }
342        catch ( final CloneNotSupportedException e )
343        {
344            throw new AssertionError( e );
345        }
346    }
347
348    /**
349     * Creates and returns a string representation of the object.
350     *
351     * @return A string representation of the object.
352     */
353    @Override
354    public String toString()
355    {
356        return ToStringBuilder.reflectionToString( this );
357    }
358
359}