Package org.xorm

Source Code of org.xorm.ClassMapping

/*
    $Header: /cvsroot/xorm/xorm/src/org/xorm/ClassMapping.java,v 1.43 2004/05/30 08:45:52 wbiggs Exp $

    This file is part of XORM.

    XORM is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    XORM is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with XORM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.xorm;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOUserException;
import javax.jdo.spi.JDOImplHelper;

import org.xorm.datastore.Table;
import org.xorm.datastore.Column;
import org.xorm.datastore.DataFetchGroup;

import org.xorm.util.FieldDescriptor;
import org.xorm.util.jdoxml.JDOClass;
import org.xorm.util.jdoxml.JDOField;

/**
* A ClassMapping provides the mapping from a Java type (class or interface)
* to a datastore type.  Each field of a Class can map to a Column in
* a Table, or may be mapped as a RelationshipMapping.
*
* A ClassMapping is also the repository for default fetch group
* information.
*/
public class ClassMapping implements I15d, Cloneable {
    private ModelMapping modelMapping;
    Class clazz;
    private Table table;
    private Class datastoreIdentityType;

    private Map fieldToColumn = new HashMap();
    private Map methodToProperty = new HashMap();
    private Map relationships = new HashMap();
    private Map inverses = new HashMap();
    private Set managedFields = new HashSet(); // contains String field names
    private Collection fields; // contains FieldDescriptors
    private Set mappedFields = new HashSet();
    private DataFetchGroup defaultFetchGroup = new DataFetchGroup();

    /** Creates a shallow clone. */
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // ignored
        }
        return null;
    }

    /**
     * Returns true if the class parameter is not a primitive type,
     * a wrapper class for a primitive type, or java.util.Date,
     * java.lang.String, java.util.Locale, java.math.BigDecimal,
     * or java.math.BigInteger.
     */
    public static boolean isUserType(Class clazz) {
        if (clazz.isArray()) {
            clazz = clazz.getComponentType();
        }
        return (!clazz.isPrimitive()
                && !clazz.equals(Boolean.class)
                && !clazz.equals(Character.class)
                && !clazz.equals(Double.class)
                && !clazz.equals(Float.class)
                && !clazz.equals(Integer.class)
                && !clazz.equals(Long.class)
                && !clazz.equals(Short.class)
                && !clazz.equals(String.class)
                && !clazz.equals(BigDecimal.class)
                && !clazz.equals(BigInteger.class)
                && !clazz.equals(Date.class)
                && !clazz.equals(Timestamp.class)
                && !clazz.equals(Time.class)
                && !clazz.equals(Locale.class)
                );
    }

    /**
     * Creates a new ClassMapping that maps the given Class.  The
     * class will be introspected and assigned relationships for all
     * properties that are of a user-defined (non-collection) type. 
     */
    public ClassMapping(ModelMapping modelMapping, Class clazz) {
        this.clazz = clazz;
        this.modelMapping = modelMapping;
        if (clazz.isInterface() || ((clazz.getModifiers() & Modifier.ABSTRACT) != 0)) {
            fields = FieldDescriptor.getFieldDescriptors(clazz);
        } else {
            JDOImplHelper helper = JDOImplHelper.getInstance();
            String[] names = helper.getFieldNames(clazz);
            Class[] types = helper.getFieldTypes(clazz);
            fields = new ArrayList();
            for (int i = 0; i < names.length; i++) {
                fields.add(new FieldDescriptor(names[i], types[i]));
            }
        }
        init();
    }

    /**
     *@see #ClassMapping(ModelMapping, Class)
     */
    public ClassMapping(ModelMapping modelMapping, Class clazz, JDOClass jdoClass) {
        this.clazz = clazz;
        this.modelMapping = modelMapping;
        fields = loadFieldDescriptors(clazz, jdoClass);
        init();
    }

    /**
     * Common code for both constructors.
     */
    private void init(){
        defaultFetchGroup = new DataFetchGroup();
        Iterator i = fields.iterator();
        while (i.hasNext()) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            // This is a kluge for now, maybe require explicit config on this?
            if (isUserType(fd.type)) {
                RelationshipMapping implicit = new RelationshipMapping();
                RelationshipMapping.Endpoint end1 = new RelationshipMapping.Endpoint();
   
                end1.setElementClass(fd.type);
                implicit.setSource(end1);
                relationships.put(fd.name, implicit);
            }

            Method read = fd.readMethod;
            if (read != null) {
                methodToProperty.put(read.getName(), fd);
            }
            Method write = fd.writeMethod;
            if (write != null) {
                methodToProperty.put(write.getName(), fd);
            }
        }
    }

    /**
     * Defines the table that will be used to map the class.
     * If table is null, defaults will be applied to create a mapping for
     * the table.  This will include a datastore identity column called
     * "xorm_pk"; other column names will be the same as the field name.
     */
    public void setTable(Table table) {
        boolean fakeTable = false;
        Column pk;
        if (table == null) {
            fakeTable = true;
            table = new Table(clazz.getName());
            pk = new Column(table, "xorm_pk");
            table.addColumn(pk);
            table.setPrimaryKey(pk);
            Iterator i = fields.iterator();
            while (i.hasNext()) {
                FieldDescriptor fd = (FieldDescriptor) i.next();
                Column c = new Column(table, fd.name);
                table.addColumn(c);
                fieldToColumn.put(fd.name, c);
            }
        } else {
            pk = table.getPrimaryKey();
            if (pk == null) {
                throw new JDOFatalUserException(I18N.msg("E_mapping_no_pk", table.getName()));
            }
        }
        defaultFetchGroup.addColumn(pk);
        this.table = table;
    }

    /**
     * Get the map of relationships.  The keys are Strings and the values
     * are RelationshipMapping objects.  A relationship is defined as a
     * -to-one or -to-many relationship to another first class user object.
     */
    public Map getRelationships() {
        return relationships;
    }

    public DataFetchGroup getDefaultFetchGroup() {
        return defaultFetchGroup;
    }

    /**
     * Gets the set of FieldDescriptors that have column mappings defined
     * in the JDO metadata file.
     */
    public Set getMappedFieldDescriptors() {
        return mappedFields;
    }

    /**
     * Gets the field descriptors found by introspection on the class
     * or interface.  This includes primitive types, user types, and
     * collection references.  The returned items are instances of
     * org.xorm.FieldDescriptor.  Not all fields found by introspection
     * are required to be mapped.
     */
    public Collection getFieldDescriptors() {
        return fields;
    }

    /**
     * Returns the set of managed field names.  This set is the union of
     * fields that are mapped and fields that are defined as relationships.
     */
    public Set getManagedFields() {
        return managedFields;
    }

    public String getInverse(String field) {
        return (String) inverses.get(field);
    }

    public void setInverse(String localField, String inverseField) {
        inverses.put(localField, inverseField);
    }

    /**
     * Retrieves the field associated with a get or set method.
     * Returns null if there is no mapping.
     */
    public String getFieldForMethod(Method method) {
        String methodName = method.getName();
        FieldDescriptor fd = (FieldDescriptor) methodToProperty.get(methodName);
        if (fd == null) return null;
        return fd.name;
    }

    /**
     * Convenience method that gets the Column mapped to a particular
     * get or set method.  Returns null for unmapped methods.
     */
    public Column getColumnForMethod(Method method) {
        return getColumn(getFieldForMethod(method));
    }
   
    /** Get the table the class is mapped to. */
    public Table getTable() {
        return table;
    }

    /** Get the class that this mapping references. */
    public Class getMappedClass() {
        return clazz;
    }

    /** Get the Column mapped to the given field. */
    public Column getColumn(String field) {
        if ("this".equals(field)) {
            return table.getPrimaryKey();
        }
        return (Column) fieldToColumn.get(field);
    }

    /**
     * Set the Column mapped to the given field.
     *
     * @exception JDOUserException if the field does not exist, or if
     * the field has a set() method but the column is marked as read-only.
     */
    public void setColumn(String field, Column column, Boolean inDefaultFetchGroup) {
        FieldDescriptor fd = getFieldDescriptor(field);
        if (column.isReadOnly() && (fd.writeMethod != null)) {
            throw new JDOUserException(I18N.msg("E_setter_ro_column", new Object[] {
                column.getName(), clazz.getName(), field }));
        }
        mappedFields.add(fd);
        fieldToColumn.put(field, column);
        managedFields.add(field);
 
        // The column selections for the default fetch group follow
        // the JDO defaults -- all non-user-defined types are included
        // unless specifically set to default-fetch-group="false".
        // For user-defined types (First-Class Objects), the column itself
        // is included by default (the foreign key, that is), but the
        // target object is not resolved unless default-fetch-group="true"
        if (!Boolean.FALSE.equals(inDefaultFetchGroup)) {
            defaultFetchGroup.addColumn(column);
            if (isUserType(fd.type) && Boolean.TRUE.equals(inDefaultFetchGroup)) {
                ClassMapping target = modelMapping.getClassMapping(fd.type);
                defaultFetchGroup.addSubgroup(column, target.getDefaultFetchGroup());
            }
        }
    }

    /**
     * Define a to-many relationship on a field.
     *
     * @exception JDOUserException if the field does not exist
     */
    public void setRelationship(String field, RelationshipMapping relation) {
        FieldDescriptor fd = getFieldDescriptor(field); // assertion
        relationships.put(field, relation);
        managedFields.add(field);
    }

    /**
     * Retrieves the FieldDescriptor associated with the given field
     * name by iterating through the collection of all FieldDescriptors
     * for this class.
     *
     * @exception JDOUserException if the named field does not exist.
     */
    public FieldDescriptor getFieldDescriptor(String field) {
        Iterator i = fields.iterator();
        while (i.hasNext()) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            if (fd.name.equals(field)) return fd;
        }
        throw new JDOUserException(I18N.msg("E_no_field", new Object[] { field, clazz.getName() }));
    }

    /**
     * Get the relationship mapped to a field. If no relationship
     * exists, returns null.
     */
    public RelationshipMapping getRelationship(String field) {
        return (RelationshipMapping) relationships.get(field);
    }

    /**
     * Convenience method to go from a get/set method to the
     * associated relationship mapping.
     */
    public RelationshipMapping getRelationshipForMethod(Method method) {
        String methodName = method.getName();
        FieldDescriptor fd = (FieldDescriptor) methodToProperty.get(methodName);
        if (fd == null) return null;
        return (RelationshipMapping) relationships.get(fd.name);
    }

    /** Returns the Java type of the field. */
    public Class getFieldType(String field) {
        return getRelationship(field).getSource().getElementClass();
    }

    /**
     * Returns the Class specified as the identity-type for a class mapping,
     * or null if none was specified.
     */
    public Class getDatastoreIdentityType() {
        return datastoreIdentityType;
    }

    public void setDatastoreIdentityType(Class datastoreIdentityType) {
        this.datastoreIdentityType = datastoreIdentityType;
    }

    /**
     * Returns a Collection of all FieldDescriptors that are configured as
     * persistent.  The Class argument may be an interface or class. Declared
     * interfaces will be used too.
     * Only fields configured in JDO mapping file are taken into account.
     * @author radek radzimir@polbox.com - 12.08.2003
     * @return a Collection<FieldDescriptor>
     * @see findLocalFieldDescriptors(Class, Map)
     */
    private Collection loadFieldDescriptors(Class clazz, JDOClass jdoClass) {
        //save properties to a map
        Map propMap = new HashMap();
        try {
            //analyse the class or interface
            BeanInfo bi;
            if(clazz.isInterface()) {
                bi = Introspector.getBeanInfo(clazz);
            } else {
                bi = Introspector.getBeanInfo(clazz, Object.class);
            }
            PropertyDescriptor [] pd = bi.getPropertyDescriptors();
            for (int i = 0; i < pd.length; i++) {
              PropertyDescriptor descriptor = pd[i];
              propMap.put(descriptor.getName(), descriptor);
            }
            //analyse interfaces
            Class [] interfaces = clazz.getInterfaces();
            for(int k=0; k<interfaces.length; k++){
              bi = Introspector.getBeanInfo(interfaces[k]);
              pd = bi.getPropertyDescriptors();
              for (int i = 0; i < pd.length; i++) {
                PropertyDescriptor descriptor = pd[i];
                String key = descriptor.getName();
                if( !propMap.containsKey(key) ){
                  propMap.put(key, descriptor);
                }
              }
            }
        } catch (IntrospectionException ex){
            throw new JDOFatalUserException("Error while introspecting class "+clazz.getName(), ex);
        }
   
        //iterate over configured properties
        Collection descriptorsColl = new Vector();
        Iterator fieldsIter = jdoClass.getFields().iterator();
        while (fieldsIter.hasNext()){
            JDOField jdoField = (JDOField)fieldsIter.next();
            String name = jdoField.getName();
            //find getter abd setter
            PropertyDescriptor descriptor = (PropertyDescriptor)propMap.get(name);
            if(descriptor == null){
                throw new JDOFatalUserException(I18N.msg("E_unknown_property", name, clazz.getName()));
            }
            FieldDescriptor fd = new FieldDescriptor(descriptor);
            descriptorsColl.add(fd);
        }
        return descriptorsColl;
    }

}
TOP

Related Classes of org.xorm.ClassMapping

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.