001: /*
002: * Copyright 2006 Le Duc Bao, Ralf Joachim
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package org.castor.ddlgen;
017:
018: import java.util.Enumeration;
019: import java.util.Vector;
020:
021: import org.castor.ddlgen.typeinfo.TypeInfo;
022: import org.exolab.castor.mapping.Mapping;
023: import org.exolab.castor.mapping.xml.ClassMapping;
024: import org.exolab.castor.mapping.xml.FieldMapping;
025:
026: /**
027: * This class handles all common tasks for manipulating Mapping document.
028: *
029: * @author <a href="mailto:leducbao AT gmail DOT com">Le Duc Bao</a>
030: * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
031: * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
032: * @since 1.1
033: */
034: public final class MappingHelper {
035: //--------------------------------------------------------------------------
036:
037: /** Mapping document. */
038: private Mapping _mapping;
039:
040: /** Type mapper. */
041: private TypeMapper _typeMapper;
042:
043: //--------------------------------------------------------------------------
044:
045: /**
046: * Get mapping document.
047: *
048: * @return Mapping document.
049: */
050: public Mapping getMapping() {
051: return _mapping;
052: }
053:
054: /**
055: * set mapping document.
056: *
057: * @param mapping Mapping document.
058: */
059: public void setMapping(final Mapping mapping) {
060: _mapping = mapping;
061: }
062:
063: /**
064: * Get type mapper.
065: *
066: * @return Type mapper.
067: */
068: public TypeMapper getTypeMapper() {
069: return _typeMapper;
070: }
071:
072: /**
073: * Set type mapper.
074: *
075: * @param typeMapper Type mapper.
076: */
077: public void setTypeMapper(final TypeMapper typeMapper) {
078: _typeMapper = typeMapper;
079: }
080:
081: //--------------------------------------------------------------------------
082:
083: /**
084: * Return the ClassMapping which associated with parameter name.
085: *
086: * @param name Name of class to get ClassMapping of.
087: * @return ClassMapping of the named class or <code>null</code> if no such
088: * ClassMapping was found.
089: */
090: public ClassMapping getClassMappingByName(final String name) {
091: Enumeration ec = _mapping.getRoot().enumerateClassMapping();
092: while (ec.hasMoreElements()) {
093: ClassMapping cm = (ClassMapping) ec.nextElement();
094: String cmName = cm.getName();
095: if ((cmName != null) && cmName.equals(name)) {
096: return cm;
097: }
098: }
099: return null;
100: }
101:
102: /**
103: * Collect sql type of all identities for class with given name. It also takes care
104: * on multiple column identities and extended classes.
105: *
106: * <pre>
107: * <mapping>
108: * <class name="myapp.OtherProductGroup" >
109: * <map-to table="other_prod_group" xml="group" />
110: * <field name="id" type="integer" identity="true">
111: * <sql name="id" type="integer"/>
112: * </field>
113: * </class>
114: *
115: * <class name="myapp.ProductGroup" identity="id">
116: * <map-to table="prod_group" xml="group" />
117: * <field name="id" type="myapp.OtherProductGroup" >
118: * <sql name="prod_id" />
119: * </field>
120: * </class>
121: *
122: * <class name="myapp.Product" identity="id">
123: * <field name="group" type="myapp.ProductGroup">
124: * <sql name="group_id" />
125: * </field>
126: * </class>
127: * </mapping>
128: * </pre>
129: *
130: * @param className Name of class to get type of identities of.
131: * @return Array of sql types of all identities.
132: * @throws GeneratorException If failed to resolve sql type of identities.
133: */
134: public synchronized String[] resolveTypeReferenceForIds(
135: final String className) throws GeneratorException {
136: ClassMapping cm = getClassMappingByName(className);
137: if (cm != null) {
138: return resolveTypeReferenceForIds(cm);
139: }
140: throw new GeneratorException("can not find class " + className);
141: }
142:
143: /**
144: * Collect sql type of all identities for a ClassMapping. It also takes care on
145: * multiple column identities and extended classes.
146: *
147: * @param cm ClassMapping to get type of identities of.
148: * @return Array of sql types of all identities.
149: * @throws GeneratorException If failed to resolve sql type of identities.
150: */
151: public String[] resolveTypeReferenceForIds(final ClassMapping cm)
152: throws GeneratorException {
153: boolean isFoundKey = false;
154:
155: String[] ids = cm.getIdentity();
156: Vector types = new Vector();
157:
158: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
159: boolean isExistFieldId = isUseFieldIdentity(cm);
160:
161: // Go through all fields.
162: while (ef.hasMoreElements()) {
163: FieldMapping fm = (FieldMapping) ef.nextElement();
164:
165: // Identity are defined at field.
166: if (isExistFieldId && fm.getIdentity()) {
167: // Get field type.
168: TypeInfo typeinfo = null;
169: String sqltype = fm.getSql().getType();
170:
171: if (sqltype != null) {
172: typeinfo = _typeMapper.getType(sqltype);
173: }
174:
175: if (typeinfo == null) {
176: String[] refRefType = resolveTypeReferenceForIds(fm
177: .getType());
178: for (int l = 0; l < refRefType.length; l++) {
179: types.add(refRefType[l]);
180: isFoundKey = true;
181: }
182: } else {
183: for (int i = 0; i < fm.getSql().getNameCount(); i++) {
184: types.add(fm.getSql().getType());
185: isFoundKey = true;
186: }
187: }
188: } else if (!isExistFieldId) {
189: // Identities are defined at class tag.
190: String fieldName = fm.getName();
191: for (int j = 0; j < ids.length; j++) {
192: // If sqlnames[i] equals ids[j] we found a reference type.
193: if (fieldName.equals(ids[j])) {
194:
195: // Check for type if this table is a reference table.
196: TypeInfo typeinfo = null;
197: String sqltype = fm.getSql().getType();
198:
199: // Verify if sqltype exists.
200: if (sqltype != null) {
201: typeinfo = _typeMapper.getType(sqltype);
202: }
203:
204: if (typeinfo == null) {
205: ClassMapping cmRef = getClassMappingByName(fm
206: .getType());
207: // If cmRef is null, the reference class could not be found
208: // so we need to use field type.
209: if (cmRef == null) {
210: typeinfo = _typeMapper.getType(fm
211: .getType());
212:
213: if (typeinfo == null) {
214: throw new TypeNotFoundException(
215: "Can't resolve type "
216: + fm.getType());
217: }
218: int count = fm.getSql().getNameCount();
219: if (count == 0) {
220: count = fm.getSql()
221: .getManyKeyCount();
222: }
223:
224: for (int l = 0; l < count; l++) {
225: types.add(fm.getType());
226: isFoundKey = true;
227: }
228:
229: } else {
230: // Resolve type for reference class.
231: String[] refRefType = resolveTypeReferenceForIds(fm
232: .getType());
233: for (int l = 0; l < refRefType.length; l++) {
234: types.add(refRefType[l]);
235: isFoundKey = true;
236: }
237: }
238: } else {
239: types.add(fm.getSql().getType());
240: isFoundKey = true;
241: }
242: }
243: }
244: }
245: }
246:
247: // If there is no identity found, looking in the extend class.
248: if (!isFoundKey && (cm.getExtends() != null)) {
249: ClassMapping extendClass = (ClassMapping) cm.getExtends();
250: String[] refRefType = resolveTypeReferenceForIds(extendClass);
251: for (int l = 0; l < refRefType.length; l++) {
252: types.add(refRefType[l]);
253: }
254: }
255:
256: return (String[]) types.toArray(new String[types.size()]);
257: }
258:
259: /**
260: * Check if identities of given ClassMapping are defined at its FieldMappings.
261: *
262: * @param cm ClassMapping to check for identity definitions at FieldMapping.
263: * @return <code>true</code> if identities are defined at fieldMapping.
264: */
265: public boolean isUseFieldIdentity(final ClassMapping cm) {
266: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
267:
268: while (ef.hasMoreElements()) {
269: FieldMapping fm = (FieldMapping) ef.nextElement();
270: if (fm.getIdentity()) {
271: return true;
272: }
273: }
274: return false;
275: }
276:
277: /**
278: * Check if given FieldMapping is an identity at given ClassMapping.
279: * <pre>
280: * <class name="myapp.ProductGroup" identity="id">
281: * <field name="id" type="integer" >
282: * <sql name="id1 id2" type="integer"/>
283: * </field>
284: * </class>
285: * </pre>
286: *
287: * @param cm ClassMapping.
288: * @param fm FieldMapping.
289: * @return <code>true</code> if FieldMapping is an identity at ClassMapping.
290: */
291: public boolean isIdentity(final ClassMapping cm,
292: final FieldMapping fm) {
293: String[] ids = cm.getIdentity();
294: String fieldName = fm.getName();
295:
296: for (int j = 0; j < ids.length; j++) {
297: if (ids[j].equalsIgnoreCase(fieldName)) {
298: return true;
299: }
300: }
301: return false;
302: }
303:
304: /**
305: * The identity definitions at class and field are alternative syntax. If
306: * both are specified the one at field should take precedence over the class
307: * one. In other words if both are specified the one at class will be
308: * ignored.
309: *
310: * @param cm ClassMapping to get sql names of identities of.
311: * @param ext Recursivly search for identities in extended ClassMappings.
312: * @return Array of sql names of identities of given ClassMapping.
313: */
314: public String[] getClassMappingSqlIdentity(final ClassMapping cm,
315: final boolean ext) {
316: Vector ids = new Vector();
317:
318: String[] identities = cm.getIdentity();
319: // If child defines identity with same or no type,
320: // use name from child and type from parent.
321: if (ext && cm.getExtends() != null && identities.length == 0) {
322: identities = getClassMappingIdentity((ClassMapping) cm
323: .getExtends());
324: }
325:
326: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
327: boolean isExistFieldId = isUseFieldIdentity(cm);
328: while (ef.hasMoreElements()) {
329: FieldMapping fm = (FieldMapping) ef.nextElement();
330: // Add all sql columns into identity list.
331: if (isExistFieldId && fm.getIdentity()) {
332: isExistFieldId = true;
333: int ncount = fm.getSql().getNameCount();
334: for (int i = 0; i < ncount; i++) {
335: ids.add(fm.getSql().getName(i));
336: }
337: } else if (!isExistFieldId) {
338: // If using class identity, find out all correspondent column names.
339: String fieldName = fm.getName();
340: for (int j = 0; j < identities.length; j++) {
341: if (fieldName.equals(identities[j])) {
342: // Check for type if this table is a reference table.
343: int ncount = fm.getSql().getNameCount();
344: for (int i = 0; i < ncount; i++) {
345: ids.add(fm.getSql().getName(i));
346: }
347: }
348: }
349: }
350: }
351:
352: // Get identities from parent.
353: if (ext && (cm.getExtends() != null) && (ids.size() == 0)) {
354: return getClassMappingSqlIdentity((ClassMapping) cm
355: .getExtends(), ext);
356: }
357:
358: return (String[]) ids.toArray(new String[ids.size()]);
359: }
360:
361: /**
362: * The identity definitions at class and field are alternative syntax. If
363: * both are specified the one at field should take precedence over the class
364: * one. In other words if both are specified the one at class will be
365: * ignored.
366: *
367: * @param cm ClassMapping to get identity names of.
368: * @return Array of identity names of given ClassMapping.
369: */
370: public String[] getClassMappingIdentity(final ClassMapping cm) {
371: Vector ids = new Vector();
372:
373: boolean isExistFieldId = false;
374: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
375: while (ef.hasMoreElements()) {
376: FieldMapping fm = (FieldMapping) ef.nextElement();
377: // Add names of all identity columns to list.
378: if (isExistFieldId && fm.getIdentity()) {
379: isExistFieldId = true;
380: ids.add(fm.getName());
381: }
382: }
383:
384: if (!isExistFieldId) {
385: String[] identities = cm.getIdentity();
386: for (int i = 0; i < identities.length; i++) {
387: ids.add(identities[i]);
388: }
389: }
390:
391: // If this is a child class, use its parents identities.
392: if ((cm.getExtends() != null) && (ids.size() == 0)) {
393: return getClassMappingIdentity((ClassMapping) cm
394: .getExtends());
395: }
396:
397: return (String[]) ids.toArray(new String[ids.size()]);
398: }
399:
400: //--------------------------------------------------------------------------
401: }
|