/* * Copyright 2006 Le Duc Bao, Ralf Joachim * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.castor.ddlgen; import java.util.Enumeration; import java.util.Vector; import org.castor.ddlgen.typeinfo.TypeInfo; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.xml.ClassMapping; import org.exolab.castor.mapping.xml.FieldMapping; /** * This class handles all common tasks for manipulating Mapping document. * * @author <a href="mailto:leducbao AT gmail DOT com">Le Duc Bao</a> * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a> * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $ * @since 1.1 */ public final class MappingHelper { //-------------------------------------------------------------------------- /** Mapping document. */ private Mapping _mapping; /** Type mapper. */ private TypeMapper _typeMapper; //-------------------------------------------------------------------------- /** * Get mapping document. * * @return Mapping document. */ public Mapping getMapping() { return _mapping; } /** * set mapping document. * * @param mapping Mapping document. */ public void setMapping(final Mapping mapping) { _mapping = mapping; } /** * Get type mapper. * * @return Type mapper. */ public TypeMapper getTypeMapper() { return _typeMapper; } /** * Set type mapper. * * @param typeMapper Type mapper. */ public void setTypeMapper(final TypeMapper typeMapper) { _typeMapper = typeMapper; } //-------------------------------------------------------------------------- /** * Return the ClassMapping which associated with parameter name. * * @param name Name of class to get ClassMapping of. * @return ClassMapping of the named class or <code>null</code> if no such * ClassMapping was found. */ public ClassMapping getClassMappingByName(final String name) { Enumeration<? extends ClassMapping> ec = _mapping.getRoot().enumerateClassMapping(); while (ec.hasMoreElements()) { ClassMapping cm = ec.nextElement(); String cmName = cm.getName(); if ((cmName != null) && cmName.equals(name)) { return cm; } } return null; } /** * Collect sql type of all identities for class with given name. It also takes care * on multiple column identities and extended classes. * * <pre> * <mapping> * <class name="myapp.OtherProductGroup" > * <map-to table="other_prod_group" xml="group" /> * <field name="id" type="integer" identity="true"> * <sql name="id" type="integer"/> * </field> * </class> * * <class name="myapp.ProductGroup" identity="id"> * <map-to table="prod_group" xml="group" /> * <field name="id" type="myapp.OtherProductGroup" > * <sql name="prod_id" /> * </field> * </class> * * <class name="myapp.Product" identity="id"> * <field name="group" type="myapp.ProductGroup"> * <sql name="group_id" /> * </field> * </class> * </mapping> * </pre> * * @param className Name of class to get type of identities of. * @return Array of sql types of all identities. * @throws GeneratorException If failed to resolve sql type of identities. */ public synchronized String[] resolveTypeReferenceForIds(final String className) throws GeneratorException { ClassMapping cm = getClassMappingByName(className); if (cm != null) { return resolveTypeReferenceForIds(cm); } throw new GeneratorException("can not find class " + className); } /** * Collect sql type of all identities for a ClassMapping. It also takes care on * multiple column identities and extended classes. * * @param cm ClassMapping to get type of identities of. * @return Array of sql types of all identities. * @throws GeneratorException If failed to resolve sql type of identities. */ public String[] resolveTypeReferenceForIds(final ClassMapping cm) throws GeneratorException { boolean isFoundKey = false; String[] ids = cm.getIdentity(); Vector<String> types = new Vector<String>(); Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); boolean isExistFieldId = isUseFieldIdentity(cm); // Go through all fields. while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); // Identity are defined at field. if (isExistFieldId && fm.getIdentity()) { // Get field type. TypeInfo typeinfo = null; String sqltype = fm.getSql().getType(); if (sqltype != null) { typeinfo = _typeMapper.getType(sqltype); } if (typeinfo == null) { String[] refRefType = resolveTypeReferenceForIds(fm.getType()); for (int l = 0; l < refRefType.length; l++) { types.add(refRefType[l]); isFoundKey = true; } } else { for (int i = 0; i < fm.getSql().getNameCount(); i++) { types.add(fm.getSql().getType()); isFoundKey = true; } } } else if (!isExistFieldId) { // Identities are defined at class tag. String fieldName = fm.getName(); for (int j = 0; j < ids.length; j++) { // If sqlnames[i] equals ids[j] we found a reference type. if (fieldName.equals(ids[j])) { // Check for type if this table is a reference table. TypeInfo typeinfo = null; String sqltype = fm.getSql().getType(); // Verify if sqltype exists. if (sqltype != null) { typeinfo = _typeMapper.getType(sqltype); } if (typeinfo == null) { ClassMapping cmRef = getClassMappingByName(fm.getType()); // If cmRef is null, the reference class could not be found // so we need to use field type. if (cmRef == null) { typeinfo = _typeMapper.getType(fm.getType()); if (typeinfo == null) { throw new TypeNotFoundException( "Can't resolve type " + fm.getType()); } int count = fm.getSql().getNameCount(); if (count == 0) { count = fm.getSql().getManyKeyCount(); } for (int l = 0; l < count; l++) { types.add(fm.getType()); isFoundKey = true; } } else { // Resolve type for reference class. String[] refRefType = resolveTypeReferenceForIds( fm.getType()); for (int l = 0; l < refRefType.length; l++) { types.add(refRefType[l]); isFoundKey = true; } } } else { types.add(fm.getSql().getType()); isFoundKey = true; } } } } } // If there is no identity found, looking in the extend class. if (!isFoundKey && (cm.getExtends() != null)) { ClassMapping extendClass = (ClassMapping) cm.getExtends(); String[] refRefType = resolveTypeReferenceForIds(extendClass); for (int l = 0; l < refRefType.length; l++) { types.add(refRefType[l]); } } return types.toArray(new String[types.size()]); } /** * Check if identities of given ClassMapping are defined at its FieldMappings. * * @param cm ClassMapping to check for identity definitions at FieldMapping. * @return <code>true</code> if identities are defined at fieldMapping. */ public boolean isUseFieldIdentity(final ClassMapping cm) { Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); if (fm.getIdentity()) { return true; } } return false; } /** * Check if given FieldMapping is an identity at given ClassMapping. * <pre> * <class name="myapp.ProductGroup" identity="id"> * <field name="id" type="integer" > * <sql name="id1 id2" type="integer"/> * </field> * </class> * </pre> * * @param cm ClassMapping. * @param fm FieldMapping. * @return <code>true</code> if FieldMapping is an identity at ClassMapping. */ public boolean isIdentity(final ClassMapping cm, final FieldMapping fm) { String[] ids = cm.getIdentity(); String fieldName = fm.getName(); for (int j = 0; j < ids.length; j++) { if (ids[j].equalsIgnoreCase(fieldName)) { return true; } } return false; } /** * The identity definitions at class and field are alternative syntax. If * both are specified the one at field should take precedence over the class * one. In other words if both are specified the one at class will be * ignored. * * @param cm ClassMapping to get sql names of identities of. * @param ext Recursivly search for identities in extended ClassMappings. * @return Array of sql names of identities of given ClassMapping. */ public String[] getClassMappingSqlIdentity(final ClassMapping cm, final boolean ext) { Vector<String> ids = new Vector<String>(); String[] identities = cm.getIdentity(); // If child defines identity with same or no type, // use name from child and type from parent. if (ext && cm.getExtends() != null && identities.length == 0) { identities = getClassMappingIdentity((ClassMapping) cm.getExtends()); } Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); boolean isExistFieldId = isUseFieldIdentity(cm); while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); // Add all sql columns into identity list. if (isExistFieldId && fm.getIdentity()) { isExistFieldId = true; int ncount = fm.getSql().getNameCount(); for (int i = 0; i < ncount; i++) { ids.add(fm.getSql().getName(i)); } } else if (!isExistFieldId) { // If using class identity, find out all correspondent column names. String fieldName = fm.getName(); for (int j = 0; j < identities.length; j++) { if (fieldName.equals(identities[j])) { // Check for type if this table is a reference table. int ncount = fm.getSql().getNameCount(); for (int i = 0; i < ncount; i++) { ids.add(fm.getSql().getName(i)); } } } } } // Get identities from parent. if (ext && (cm.getExtends() != null) && (ids.size() == 0)) { return getClassMappingSqlIdentity((ClassMapping) cm.getExtends(), ext); } return ids.toArray(new String[ids.size()]); } /** * The identity definitions at class and field are alternative syntax. If * both are specified the one at field should take precedence over the class * one. In other words if both are specified the one at class will be * ignored. * * @param cm ClassMapping to get identity names of. * @return Array of identity names of given ClassMapping. */ public String[] getClassMappingIdentity(final ClassMapping cm) { Vector<String> ids = new Vector<String>(); boolean isExistFieldId = false; Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); // Add names of all identity columns to list. if (isExistFieldId && fm.getIdentity()) { isExistFieldId = true; ids.add(fm.getName()); } } if (!isExistFieldId) { String[] identities = cm.getIdentity(); for (int i = 0; i < identities.length; i++) { ids.add(identities[i]); } } // If this is a child class, use its parents identities. if ((cm.getExtends() != null) && (ids.size() == 0)) { return getClassMappingIdentity((ClassMapping) cm.getExtends()); } return ids.toArray(new String[ids.size()]); } //-------------------------------------------------------------------------- }