001/* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 * 
007 * Project Info:  http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 * 
027 * ------------------------------
028 * AbstractObjectDescription.java
029 * ------------------------------
030 * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031 *
032 * Original Author:  Thomas Morgner;
033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: AbstractObjectDescription.java,v 1.3 2005/11/14 10:58:37 mungady Exp $
036 *
037 * Changes (from 19-Feb-2003)
038 * -------------------------
039 * 19-Feb-2003 : Added standard header and Javadocs (DG);
040 * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
041 *
042 */
043
044package org.jfree.xml.factory.objects;
045
046import java.util.ArrayList;
047import java.util.Collections;
048import java.util.HashMap;
049import java.util.Iterator;
050
051import org.jfree.util.Configuration;
052import org.jfree.util.Log;
053import org.jfree.util.ReadOnlyIterator;
054
055/**
056 * An abstract base class for object descriptions.
057 *
058 * @author Thomas Morgner.
059 */
060public abstract class AbstractObjectDescription implements ObjectDescription, Cloneable {
061
062    /** The class. */
063    private Class className;
064
065    /** Storage for parameters. */
066    private HashMap parameters;
067
068    /** Storage for parameter definitions. */
069    private HashMap parameterDefs;
070
071    /** The configuration for the object description. */
072    private Configuration config;
073
074    /**
075     * Creates a new object description.
076     *
077     * @param className  the class.
078     */
079    public AbstractObjectDescription(final Class className) {
080        this.className = className;
081        this.parameters = new HashMap();
082        this.parameterDefs = new HashMap();
083    }
084
085    /**
086     * Returns a parameter class.
087     *
088     * @param name  the parameter definition.
089     *
090     * @return The class.
091     */
092    public Class getParameterDefinition(final String name) {
093        return (Class) this.parameterDefs.get(name);
094    }
095
096    /**
097     * Sets the class for a parameter.
098     *
099     * @param name  the parameter name.
100     * @param obj  the parameter class.
101     */
102    public void setParameterDefinition(final String name, final Class obj) {
103        if (obj == null) {
104            this.parameterDefs.remove(name);
105        }
106        else {
107            this.parameterDefs.put(name, obj);
108        }
109    }
110
111    /**
112     * Converts primitives to corresponding object class.
113     *
114     * @param obj  the class.
115     *
116     * @return The class.
117     */
118    public static Class convertPrimitiveClass(final Class obj) {
119        if (!obj.isPrimitive()) {
120            return obj;
121        }
122        if (obj == Boolean.TYPE) {
123            return Boolean.class;
124        }
125        if (obj == Byte.TYPE) {
126            return Byte.class;
127        }
128        if (obj == Character.TYPE) {
129            return Character.class;
130        }
131        if (obj == Short.TYPE) {
132            return Short.class;
133        }
134        if (obj == Integer.TYPE) {
135            return Integer.class;
136        }
137        if (obj == Long.TYPE) {
138            return Long.class;
139        }
140        if (obj == Float.TYPE) {
141            return Float.class;
142        }
143        if (obj == Double.TYPE) {
144            return Double.class;
145        }
146        throw new IllegalArgumentException("Class 'void' is not allowed here");
147    }
148
149    /**
150     * Sets a parameter.
151     *
152     * @param name  the name.
153     * @param value  the value.
154     */
155    public void setParameter(final String name, final Object value) {
156        if (getParameterDefinition(name) == null) {
157            throw new IllegalArgumentException("No such Parameter defined: " + name
158                + " in class " + getObjectClass());
159        }
160        final Class parameterClass = convertPrimitiveClass(getParameterDefinition(name));
161        if (!parameterClass.isAssignableFrom(value.getClass())) {
162            throw new ClassCastException("In Object " + getObjectClass()
163                + ": Value is not assignable: " + value.getClass()
164                + " is not assignable from " + parameterClass);
165        }
166        this.parameters.put(name, value);
167    }
168
169    /**
170     * Returns an iterator for the parameter names.
171     *
172     * @return The iterator.
173     */
174    public synchronized Iterator getParameterNames() {
175        final ArrayList parameterNames = new ArrayList(this.parameterDefs.keySet());
176        Collections.sort(parameterNames);
177        return new ReadOnlyIterator (parameterNames.iterator());
178    }
179
180    /**
181     * Returns an iterator for the parameter names.
182     *
183     * @return The iterator.
184     */
185    protected Iterator getDefinedParameterNames() {
186        return new ReadOnlyIterator (this.parameters.keySet().iterator());
187    }
188
189    /**
190     * Returns a parameter value.
191     *
192     * @param name  the parameter name.
193     *
194     * @return The parameter value.
195     */
196    public Object getParameter(final String name) {
197        return this.parameters.get(name);
198    }
199
200    /**
201     * Returns the class for the object.
202     *
203     * @return The class.
204     */
205    public Class getObjectClass() {
206        return this.className;
207    }
208
209    /**
210     * Returns a cloned instance of the object description. The contents
211     * of the parameter objects collection are cloned too, so that any
212     * already defined parameter value is copied to the new instance.
213     * <p>
214     * Parameter definitions are not cloned, as they are considered read-only.
215     * <p>
216     * The newly instantiated object description is not configured. If it
217     * need to be configured, then you have to call configure on it.
218     *
219     * @return A cloned instance.
220     */
221    public ObjectDescription getInstance() {
222        try {
223            final AbstractObjectDescription c = (AbstractObjectDescription) super.clone();
224            c.parameters = (HashMap) this.parameters.clone();
225            return c;
226        }
227        catch (Exception e) {
228            Log.error("Should not happen: Clone Error: ", e);
229            return null;
230        }
231    }
232
233
234    /**
235     * Returns a cloned instance of the object description. The contents
236     * of the parameter objects collection are cloned too, so that any
237     * already defined parameter value is copied to the new instance.
238     * <p>
239     * Parameter definitions are not cloned, as they are considered read-only.
240     * <p>
241     * The newly instantiated object description is not configured. If it
242     * need to be configured, then you have to call configure on it.
243     *
244     * @return A cloned instance.
245     */
246    public ObjectDescription getUnconfiguredInstance() {
247        try {
248            final AbstractObjectDescription c = (AbstractObjectDescription) super.clone();
249            c.parameters = (HashMap) this.parameters.clone();
250            c.config = null;
251            return c;
252        }
253        catch (Exception e) {
254            Log.error("Should not happen: Clone Error: ", e);
255            return null;
256        }
257    }
258
259    /**
260     * Configures this factory. The configuration contains several keys and
261     * their defined values. The given reference to the configuration object
262     * will remain valid until the report parsing or writing ends.
263     * <p>
264     * The configuration contents may change during the reporting.
265     *
266     * @param config the configuration, never null
267     */
268    public void configure(final Configuration config) {
269        if (config == null) {
270            throw new NullPointerException("The given configuration is null");
271        }
272        this.config = config;
273    }
274
275    /**
276     * Returns the configuration for that object description.
277     *
278     * @return the configuration or null, if not yet set.
279     */
280    public Configuration getConfig() {
281        return this.config;
282    }
283
284    /**
285     * Tests for equality.
286     * 
287     * @param o  the object to test.
288     * 
289     * @return A boolean.
290     */
291    public boolean equals(final Object o) {
292        if (this == o) {
293            return true;
294        }
295        if (!(o instanceof AbstractObjectDescription)) {
296            return false;
297        }
298
299        final AbstractObjectDescription abstractObjectDescription = (AbstractObjectDescription) o;
300
301        if (!this.className.equals(abstractObjectDescription.className)) {
302            return false;
303        }
304
305        return true;
306    }
307
308    /**
309     * Returns a hash code for the object.
310     * 
311     * @return The hash code.
312     */
313    public int hashCode() {
314        return this.className.hashCode();
315    }
316}