001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.meta;
020:
021: import java.io.File;
022: import java.util.Collections;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Collection;
028: import java.util.ArrayList;
029:
030: import org.apache.commons.lang.StringUtils;
031: import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
032: import org.apache.openjpa.jdbc.schema.Column;
033: import org.apache.openjpa.jdbc.schema.ForeignKey;
034: import org.apache.openjpa.jdbc.schema.Schema;
035: import org.apache.openjpa.jdbc.schema.SchemaGroup;
036: import org.apache.openjpa.jdbc.schema.Table;
037: import org.apache.openjpa.jdbc.schema.Unique;
038: import org.apache.openjpa.lib.meta.SourceTracker;
039: import org.apache.openjpa.lib.util.Localizer;
040: import org.apache.openjpa.lib.xml.Commentable;
041: import org.apache.openjpa.util.UserException;
042:
043: /**
044: * Information about the mapping from a class to the schema, in raw form.
045: * The columns and tables used in mapping info will not be part of the
046: * {@link SchemaGroup} used at runtime. Rather, they will be structs
047: * with the relevant pieces of information filled in.
048: *
049: * @author Abe White
050: */
051: public class ClassMappingInfo extends MappingInfo implements
052: SourceTracker, Commentable {
053:
054: private static final Localizer _loc = Localizer
055: .forPackage(ClassMappingInfo.class);
056:
057: private String _className = Object.class.getName();
058: private String _tableName = null;
059: private String _schemaName = null;
060: private boolean _joined = false;
061: private Map _seconds = null;
062: private String _subStrat = null;
063: private File _file = null;
064: private int _srcType = SRC_OTHER;
065: private String[] _comments = null;
066: private Collection _uniques = null;//Unique
067:
068: /**
069: * The described class name.
070: */
071: public String getClassName() {
072: return _className;
073: }
074:
075: /**
076: * The described class name.
077: */
078: public void setClassName(String name) {
079: _className = name;
080: }
081:
082: /**
083: * The default strategy for subclasses in this hierarchy.
084: */
085: public String getHierarchyStrategy() {
086: return _subStrat;
087: }
088:
089: /**
090: * The default strategy for subclasses in this hierarchy.
091: */
092: public void setHierarchyStrategy(String strategy) {
093: _subStrat = strategy;
094: }
095:
096: /**
097: * The given table name.
098: */
099: public String getTableName() {
100: return _tableName;
101: }
102:
103: /**
104: * The given table name.
105: */
106: public void setTableName(String table) {
107: _tableName = table;
108: }
109:
110: /**
111: * The default schema name for unqualified tables.
112: */
113: public String getSchemaName() {
114: return _schemaName;
115: }
116:
117: /**
118: * The default schema name for unqualified tables.
119: */
120: public void setSchemaName(String schema) {
121: _schemaName = schema;
122: }
123:
124: /**
125: * Whether there is a join to the superclass table.
126: */
127: public boolean isJoinedSubclass() {
128: return _joined;
129: }
130:
131: /**
132: * Whether there is a join to the superclass table.
133: */
134: public void setJoinedSubclass(boolean joined) {
135: _joined = joined;
136: }
137:
138: /**
139: * Return the class-level joined tables.
140: */
141: public String[] getSecondaryTableNames() {
142: if (_seconds == null)
143: return new String[0];
144: return (String[]) _seconds.keySet().toArray(new String[] {});
145: }
146:
147: /**
148: * We allow fields to reference class-level joins using just the table
149: * name, whereas the class join might have schema, etc information.
150: * This method returns the name of the given table as listed in a
151: * class-level join, or the given name if no join exists.
152: */
153: public String getSecondaryTableName(String tableName) {
154: // if no secondary table joins, bad table name, exact match,
155: // or an already-qualified table name, nothing to do
156: if (_seconds == null || tableName == null
157: || _seconds.containsKey(tableName)
158: || tableName.indexOf('.') != -1)
159: return tableName;
160:
161: // decide which class-level join table is best match
162: String best = tableName;
163: int pts = 0;
164: String fullJoin;
165: String join;
166: int idx;
167: for (Iterator itr = _seconds.keySet().iterator(); itr.hasNext();) {
168: // award a caseless match without schema 2 points
169: fullJoin = (String) itr.next();
170: idx = fullJoin.lastIndexOf('.');
171: if (idx == -1 && pts < 2
172: && fullJoin.equalsIgnoreCase(tableName)) {
173: best = fullJoin;
174: pts = 2;
175: } else if (idx == -1)
176: continue;
177:
178: // immediately return an exact match with schema
179: join = fullJoin.substring(idx + 1);
180: if (join.equals(tableName))
181: return fullJoin;
182:
183: // caseless match with schema worth 1 point
184: if (pts < 1 && join.equalsIgnoreCase(tableName)) {
185: best = fullJoin;
186: pts = 1;
187: }
188: }
189: return best;
190: }
191:
192: /**
193: * Return any columns defined for the given class level join, or empty
194: * list if none.
195: */
196: public List getSecondaryTableJoinColumns(String tableName) {
197: if (_seconds == null || tableName == null)
198: return Collections.EMPTY_LIST;
199:
200: // get the columns for the join with the best match for table name
201: List cols = (List) _seconds
202: .get(getSecondaryTableName(tableName));
203: if (cols == null) {
204: // possible that given table has extra info the join table
205: // doesn't have; strip it
206: int idx = tableName.lastIndexOf('.');
207: if (idx != -1) {
208: tableName = tableName.substring(idx + 1);
209: cols = (List) _seconds
210: .get(getSecondaryTableName(tableName));
211: }
212: }
213: return (cols == null) ? Collections.EMPTY_LIST : cols;
214: }
215:
216: /**
217: * Declare the given class-level join.
218: */
219: public void setSecondaryTableJoinColumns(String tableName, List cols) {
220: if (cols == null)
221: cols = Collections.EMPTY_LIST;
222: if (_seconds == null)
223: _seconds = new HashMap();
224: _seconds.put(tableName, cols);
225: }
226:
227: /**
228: * Return the table for the given class.
229: */
230: public Table getTable(final ClassMapping cls, boolean adapt) {
231: Table t = createTable(cls, new TableDefaults() {
232: public String get(Schema schema) {
233: // delay this so that we don't do schema reflection for unique
234: // table name unless necessary
235: return cls.getMappingRepository().getMappingDefaults()
236: .getTableName(cls, schema);
237: }
238: }, _schemaName, _tableName, adapt);
239: t.setComment(cls.getTypeAlias() == null ? cls
240: .getDescribedType().getName() : cls.getTypeAlias());
241: return t;
242: }
243:
244: /**
245: * Return the datastore identity columns for the given class, based on the
246: * given templates.
247: */
248: public Column[] getDataStoreIdColumns(ClassMapping cls,
249: Column[] tmplates, Table table, boolean adapt) {
250: cls.getMappingRepository().getMappingDefaults()
251: .populateDataStoreIdColumns(cls, table, tmplates);
252: return createColumns(cls, "datastoreid", tmplates, table, adapt);
253: }
254:
255: /**
256: * Return the join from this class to its superclass. The table for
257: * this class must be set.
258: */
259: public ForeignKey getSuperclassJoin(final ClassMapping cls,
260: Table table, boolean adapt) {
261: ClassMapping sup = cls.getJoinablePCSuperclassMapping();
262: if (sup == null)
263: return null;
264:
265: ForeignKeyDefaults def = new ForeignKeyDefaults() {
266: public ForeignKey get(Table local, Table foreign,
267: boolean inverse) {
268: return cls.getMappingRepository().getMappingDefaults()
269: .getJoinForeignKey(cls, local, foreign);
270: }
271:
272: public void populate(Table local, Table foreign,
273: Column col, Object target, boolean inverse,
274: int pos, int cols) {
275: cls.getMappingRepository().getMappingDefaults()
276: .populateJoinColumn(cls, local, foreign, col,
277: target, pos, cols);
278: }
279: };
280: return createForeignKey(cls, "superclass", getColumns(), def,
281: table, cls, sup, false, adapt);
282: }
283:
284: /**
285: * Synchronize internal information with the mapping data for the given
286: * class.
287: */
288: public void syncWith(ClassMapping cls) {
289: clear(false);
290:
291: ClassMapping sup = cls.getMappedPCSuperclassMapping();
292: if (cls.getTable() != null
293: && (sup == null || sup.getTable() != cls.getTable()))
294: _tableName = cls.getMappingRepository().getDBDictionary()
295: .getFullName(cls.getTable(), true);
296:
297: // set io before syncing cols
298: setColumnIO(cls.getColumnIO());
299: if (cls.getJoinForeignKey() != null && sup != null
300: && sup.getTable() != null)
301: syncForeignKey(cls, cls.getJoinForeignKey(),
302: cls.getTable(), sup.getTable());
303: else if (cls.getIdentityType() == ClassMapping.ID_DATASTORE)
304: syncColumns(cls, cls.getPrimaryKeyColumns(), false);
305:
306: // record inheritance strategy if class does not use default strategy
307: // for base classes, and for all subclasses so we can be sure subsequent
308: // mapping runs don't think subclass is unmapped
309: String strat = (cls.getStrategy() == null) ? null : cls
310: .getStrategy().getAlias();
311: if (strat != null
312: && (cls.getPCSuperclass() != null || !FullClassStrategy.ALIAS
313: .equals(strat)))
314: setStrategy(strat);
315: }
316:
317: public boolean hasSchemaComponents() {
318: return super .hasSchemaComponents() || _tableName != null;
319: }
320:
321: protected void clear(boolean canFlags) {
322: super .clear(canFlags);
323: _tableName = null;
324: }
325:
326: public void copy(MappingInfo info) {
327: super .copy(info);
328: if (!(info instanceof ClassMappingInfo))
329: return;
330:
331: ClassMappingInfo cinfo = (ClassMappingInfo) info;
332: if (_tableName == null)
333: _tableName = cinfo.getTableName();
334: if (_subStrat == null)
335: _subStrat = cinfo.getHierarchyStrategy();
336: if (cinfo._seconds != null) {
337: if (_seconds == null)
338: _seconds = new HashMap();
339: Object key;
340: for (Iterator itr = cinfo._seconds.keySet().iterator(); itr
341: .hasNext();) {
342: key = itr.next();
343: if (!_seconds.containsKey(key))
344: _seconds.put(key, cinfo._seconds.get(key));
345: }
346: }
347: if (cinfo._uniques != null)
348: _uniques = new ArrayList(cinfo._uniques);
349: }
350:
351: public void addUnique(Unique unique) {
352: if (unique == null)
353: return;
354: if (_uniques == null)
355: _uniques = new ArrayList();
356: _uniques.add(unique);
357: }
358:
359: public Unique[] getUniques() {
360: return (_uniques == null) ? new Unique[0] : (Unique[]) _uniques
361: .toArray(new Unique[_uniques.size()]);
362: }
363:
364: public Unique[] getUniques(ClassMapping cm, boolean adapt) {
365: if (_uniques == null || _uniques.isEmpty())
366: return new Unique[0];
367:
368: Iterator uniqueConstraints = _uniques.iterator();
369: Table table = cm.getTable();
370: Collection result = new ArrayList();
371: while (uniqueConstraints.hasNext()) {
372: Unique template = (Unique) uniqueConstraints.next();
373: Column[] templateColumns = template.getColumns();
374: Column[] uniqueColumns = new Column[templateColumns.length];
375: boolean missingColumn = true;
376: for (int i = 0; i < uniqueColumns.length; i++) {
377: String columnName = templateColumns[i].getName();
378: Column uniqueColumn = table.getColumn(columnName);
379: missingColumn = (uniqueColumn == null);
380: if (missingColumn) {
381: throw new UserException(_loc.get(
382: "missing-unique-column", cm, table,
383: columnName));
384: }
385: uniqueColumns[i] = uniqueColumn;
386: }
387: Unique unique = super .createUnique(cm, "unique", template,
388: uniqueColumns, adapt);
389: if (unique != null)
390: result.add(unique);
391: }
392: return (Unique[]) result.toArray(new Unique[result.size()]);
393: }
394:
395: public File getSourceFile() {
396: return _file;
397: }
398:
399: public Object getSourceScope() {
400: return null;
401: }
402:
403: public int getSourceType() {
404: return _srcType;
405: }
406:
407: public void setSource(File file, int srcType) {
408: _file = file;
409: _srcType = srcType;
410: }
411:
412: public String getResourceName() {
413: return _className;
414: }
415:
416: public String[] getComments() {
417: return (_comments == null) ? EMPTY_COMMENTS : _comments;
418: }
419:
420: public void setComments(String[] comments) {
421: _comments = comments;
422: }
423: }
|