001: package org.apache.ojb.broker.ant;
002:
003: /* Copyright 2004-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Set;
026: import java.util.TreeMap;
027:
028: import org.apache.commons.beanutils.DynaBean;
029: import org.apache.ddlutils.model.Column;
030: import org.apache.ddlutils.model.Database;
031: import org.apache.ddlutils.model.Table;
032: import org.apache.ojb.broker.metadata.ClassDescriptor;
033: import org.apache.ojb.broker.metadata.CollectionDescriptor;
034: import org.apache.ojb.broker.metadata.DescriptorRepository;
035: import org.apache.ojb.broker.metadata.FieldDescriptor;
036:
037: /**
038: * Provides a model derived from {@link org.apache.ojb.broker.metadata.DescriptorRepository} that
039: * is preprocessed for data handling (inserting data, generating data dtd).
040: *
041: * @author Thomas Dudziak
042: */
043: public class PreparedModel {
044: /** The database model. */
045: private Database _schema;
046: /** Maps dtd elements to tables */
047: private TreeMap _elementToTable = new TreeMap();
048: /** Maps dtd elements to lists of class descriptors (which all map to the same table) */
049: private HashMap _elementToClassDescriptors = new HashMap();
050: /** Maps dtd elements to colum maps which in turn map attribute names to columns */
051: private HashMap _elementToColumnMap = new HashMap();
052: /** Maps dtd elements to maps that specify which attributes are required */
053: private HashMap _elementToRequiredAttributesMap = new HashMap();
054:
055: public PreparedModel(DescriptorRepository model, Database schema) {
056: _schema = schema;
057: prepareModel(model);
058: }
059:
060: public Iterator getElementNames() {
061: return _elementToTable.keySet().iterator();
062: }
063:
064: public Iterator getAttributeNames(String elementName) {
065: Map columns = getColumnsFor(elementName);
066:
067: return columns == null ? null : columns.keySet().iterator();
068: }
069:
070: public Map getRequiredAttributes(String elementName) {
071: return (Map) _elementToRequiredAttributesMap.get(elementName);
072: }
073:
074: public boolean isRequired(String elementName, String attributeName) {
075: Map requiredAttributes = getRequiredAttributes(elementName);
076:
077: if (requiredAttributes == null) {
078: return false;
079: } else {
080: Boolean status = (Boolean) requiredAttributes
081: .get(attributeName);
082:
083: return status == null ? false : status.booleanValue();
084: }
085: }
086:
087: public Table getTableFor(String elementName) {
088: return (Table) _elementToTable.get(elementName);
089: }
090:
091: /**
092: * Creates a dyna bean for the table associated to the given element.
093: *
094: * @param elementName The element name
095: * @return The dyna bean
096: */
097: public DynaBean createBeanFor(String elementName) {
098: return _schema.createDynaBeanFor(getTableFor(elementName));
099: }
100:
101: public List getClassDescriptorsMappingTo(String elementName) {
102: return (List) _elementToClassDescriptors.get(elementName);
103: }
104:
105: public Map getColumnsFor(String elementName) {
106: return (Map) _elementToColumnMap.get(elementName);
107: }
108:
109: public Column getColumnFor(String elementName, String attrName) {
110: Map columns = getColumnsFor(elementName);
111:
112: if (columns == null) {
113: return null;
114: } else {
115: return (Column) columns.get(attrName);
116: }
117: }
118:
119: /**
120: * Prepares a representation of the model that is easier accessible for our purposes.
121: *
122: * @param model The original model
123: * @return The model representation
124: */
125: private void prepareModel(DescriptorRepository model) {
126: TreeMap result = new TreeMap();
127:
128: for (Iterator it = model.getDescriptorTable().values()
129: .iterator(); it.hasNext();) {
130: ClassDescriptor classDesc = (ClassDescriptor) it.next();
131:
132: if (classDesc.getFullTableName() == null) {
133: // not mapped to a database table
134: continue;
135: }
136:
137: String elementName = getElementName(classDesc);
138: Table mappedTable = getTableFor(elementName);
139: Map columnsMap = getColumnsFor(elementName);
140: Map requiredAttributes = getRequiredAttributes(elementName);
141: List classDescs = getClassDescriptorsMappingTo(elementName);
142:
143: if (mappedTable == null) {
144: mappedTable = _schema.findTable(classDesc
145: .getFullTableName());
146: if (mappedTable == null) {
147: continue;
148: }
149: columnsMap = new TreeMap();
150: requiredAttributes = new HashMap();
151: classDescs = new ArrayList();
152: _elementToTable.put(elementName, mappedTable);
153: _elementToClassDescriptors.put(elementName, classDescs);
154: _elementToColumnMap.put(elementName, columnsMap);
155: _elementToRequiredAttributesMap.put(elementName,
156: requiredAttributes);
157: }
158: classDescs.add(classDesc);
159: extractAttributes(classDesc, mappedTable, columnsMap,
160: requiredAttributes);
161: }
162: extractIndirectionTables(model, _schema);
163: }
164:
165: private void extractAttributes(ClassDescriptor classDesc,
166: Table mappedTable, Map columnsMap, Map requiredColumnsMap) {
167: FieldDescriptor[] fieldDescs = classDesc.getFieldDescriptions();
168:
169: if (fieldDescs != null) {
170: for (int idx = 0; idx < fieldDescs.length; idx++) {
171: Column column = mappedTable.findColumn(fieldDescs[idx]
172: .getColumnName());
173:
174: if (column != null) {
175: // we'll check whether another field (of not necessarily the same name)
176: // already maps to this column; if this is the case, we're ignoring
177: // this field
178: boolean alreadyMapped = false;
179:
180: for (Iterator mappedColumnsIt = columnsMap.values()
181: .iterator(); mappedColumnsIt.hasNext();) {
182: if (column.equals(mappedColumnsIt.next())) {
183: alreadyMapped = true;
184: break;
185: }
186: }
187: if (!alreadyMapped) {
188: String shortAttrName = getShortAttributeName(fieldDescs[idx]
189: .getAttributeName());
190:
191: columnsMap.put(shortAttrName, column);
192: requiredColumnsMap
193: .put(shortAttrName, fieldDescs[idx]
194: .isPrimaryKey() ? Boolean.TRUE
195: : Boolean.FALSE);
196: }
197: }
198: }
199: }
200: }
201:
202: /**
203: * Extracts indirection tables from the given class descriptor, and adds elements
204: * for them. In contrast to normal elements, for indirection tables the element name
205: * matches the table name, and the attribute names match the column names.
206: *
207: * @param model The model
208: * @param elements The elements
209: */
210: private void extractIndirectionTables(DescriptorRepository model,
211: Database schema) {
212: HashMap indirectionTables = new HashMap();
213:
214: // first we gather all participants for each m:n relationship
215: for (Iterator classDescIt = model.getDescriptorTable().values()
216: .iterator(); classDescIt.hasNext();) {
217: ClassDescriptor classDesc = (ClassDescriptor) classDescIt
218: .next();
219:
220: for (Iterator collDescIt = classDesc
221: .getCollectionDescriptors().iterator(); collDescIt
222: .hasNext();) {
223: CollectionDescriptor collDesc = (CollectionDescriptor) collDescIt
224: .next();
225: String indirTable = collDesc.getIndirectionTable();
226:
227: if ((indirTable != null) && (indirTable.length() > 0)) {
228: Set columns = (Set) indirectionTables
229: .get(indirTable);
230:
231: if (columns == null) {
232: columns = new HashSet();
233: indirectionTables.put(indirTable, columns);
234: }
235: columns.addAll(Arrays.asList(collDesc
236: .getFksToThisClass()));
237: columns.addAll(Arrays.asList(collDesc
238: .getFksToItemClass()));
239: }
240: }
241: }
242: if (indirectionTables.isEmpty()) {
243: // nothing to do
244: return;
245: }
246:
247: for (Iterator it = indirectionTables.keySet().iterator(); it
248: .hasNext();) {
249: String tableName = (String) it.next();
250: Set columns = (Set) indirectionTables.get(tableName);
251: String elementName = tableName;
252:
253: for (Iterator classDescIt = model.getDescriptorTable()
254: .values().iterator(); classDescIt.hasNext();) {
255: ClassDescriptor classDesc = (ClassDescriptor) classDescIt
256: .next();
257:
258: if (tableName.equals(classDesc.getFullTableName())) {
259: elementName = getElementName(classDesc);
260:
261: FieldDescriptor[] fieldDescs = classDesc
262: .getFieldDescriptions();
263:
264: if (fieldDescs != null) {
265: for (int idx = 0; idx < fieldDescs.length; idx++) {
266: columns.remove(fieldDescs[idx]
267: .getColumnName());
268: }
269: }
270: }
271: }
272:
273: Table mappedTable = getTableFor(elementName);
274: Map columnsMap = getColumnsFor(elementName);
275: Map requiredAttributes = getRequiredAttributes(elementName);
276:
277: if (mappedTable == null) {
278: mappedTable = schema.findTable(elementName);
279: if (mappedTable == null) {
280: continue;
281: }
282: columnsMap = new TreeMap();
283: requiredAttributes = new HashMap();
284: _elementToTable.put(elementName, mappedTable);
285: _elementToColumnMap.put(elementName, columnsMap);
286: _elementToRequiredAttributesMap.put(elementName,
287: requiredAttributes);
288: }
289: for (Iterator columnIt = columns.iterator(); columnIt
290: .hasNext();) {
291: String columnName = (String) columnIt.next();
292: Column column = mappedTable.findColumn(columnName);
293:
294: if (column != null) {
295: columnsMap.put(columnName, column);
296: requiredAttributes.put(columnName, Boolean.TRUE);
297: }
298: }
299: }
300: }
301:
302: /**
303: * Returns the element name for the class descriptor which is the adjusted short (unqualified) class
304: * name. Also takes care that the element name does not clash with another class of the same short
305: * name that maps to a different table though.
306: *
307: * @param classDesc The class descriptor
308: * @return The element name
309: */
310: private String getElementName(ClassDescriptor classDesc) {
311: String elementName = classDesc.getClassNameOfObject().replace(
312: '$', '_');
313:
314: elementName = elementName.substring(elementName
315: .lastIndexOf('.') + 1);
316:
317: Table table = getTableFor(elementName);
318: int suffix = 0;
319:
320: while ((table != null)
321: && !table.getName()
322: .equals(classDesc.getFullTableName())) {
323: ++suffix;
324: table = getTableFor(elementName + "-" + suffix);
325: }
326: if (suffix > 0) {
327: elementName += "-" + suffix;
328: }
329:
330: return elementName;
331: }
332:
333: /**
334: * Adjusts the local attribute name (the part after the last '::' for nested fields).
335: *
336: * @param attrName The original attribute name
337: * @return The local attribute name
338: */
339: private String getShortAttributeName(String attrName) {
340: return attrName.substring(attrName.lastIndexOf(':') + 1);
341: }
342: }
|