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.sql.SQLException;
022: import java.util.HashMap;
023: import java.util.Map;
024:
025: import org.apache.openjpa.jdbc.schema.Column;
026: import org.apache.openjpa.jdbc.schema.ColumnIO;
027: import org.apache.openjpa.jdbc.schema.ForeignKey;
028: import org.apache.openjpa.jdbc.schema.Index;
029: import org.apache.openjpa.jdbc.schema.Schemas;
030: import org.apache.openjpa.jdbc.schema.Table;
031: import org.apache.openjpa.jdbc.schema.Unique;
032: import org.apache.openjpa.jdbc.sql.Row;
033: import org.apache.openjpa.jdbc.sql.Select;
034: import org.apache.openjpa.kernel.OpenJPAStateManager;
035: import org.apache.openjpa.lib.util.Localizer;
036: import org.apache.openjpa.meta.JavaTypes;
037: import org.apache.openjpa.meta.ValueMetaDataImpl;
038: import org.apache.openjpa.util.InternalException;
039: import org.apache.openjpa.util.MetaDataException;
040:
041: /**
042: * Standalone {@link ValueMapping} implementation.
043: *
044: * @author Abe White
045: * @since 0.4.0
046: */
047: public class ValueMappingImpl extends ValueMetaDataImpl implements
048: ValueMapping {
049:
050: private static final Localizer _loc = Localizer
051: .forPackage(ValueMappingImpl.class);
052:
053: private ValueMappingInfo _info;
054: private ValueHandler _handler = null;
055: private ClassMapping[] _typeArr = null;
056:
057: private Column[] _cols = Schemas.EMPTY_COLUMNS;
058: private ColumnIO _io = null;
059: private ForeignKey _fk = null;
060: private Map _targetFKs = null;
061: private Index _idx = null;
062: private Unique _unq = null;
063: private int _join = JOIN_FORWARD;
064: private boolean _criteria = false;
065: private int _poly = POLY_TRUE;
066:
067: /**
068: * Constructor. Supply owning mapping.
069: */
070: public ValueMappingImpl(FieldMapping owner) {
071: super (owner);
072: _info = owner.getMappingRepository().newMappingInfo(this );
073: _info.setUseClassCriteria(owner.getMappingRepository()
074: .getMappingDefaults().useClassCriteria());
075: }
076:
077: /**
078: * Constructor for deserialization.
079: */
080: protected ValueMappingImpl() {
081: super ();
082: }
083:
084: public ValueMappingInfo getValueInfo() {
085: return _info;
086: }
087:
088: public ValueHandler getHandler() {
089: return _handler;
090: }
091:
092: public void setHandler(ValueHandler handler) {
093: _handler = handler;
094: }
095:
096: public MappingRepository getMappingRepository() {
097: return (MappingRepository) getRepository();
098: }
099:
100: public FieldMapping getFieldMapping() {
101: return (FieldMapping) getFieldMetaData();
102: }
103:
104: public ClassMapping getTypeMapping() {
105: return (ClassMapping) getTypeMetaData();
106: }
107:
108: public ClassMapping getDeclaredTypeMapping() {
109: return (ClassMapping) getDeclaredTypeMetaData();
110: }
111:
112: public ClassMapping getEmbeddedMapping() {
113: return (ClassMapping) getEmbeddedMetaData();
114: }
115:
116: public FieldMapping getValueMappedByMapping() {
117: return (FieldMapping) getValueMappedByMetaData();
118: }
119:
120: public Column[] getColumns() {
121: if (_cols.length != 0)
122: return _cols;
123: if (_fk != null)
124: return _fk.getColumns();
125: if (getValueMappedBy() != null)
126: return getValueMappedByMapping().getColumns();
127: return _cols;
128: }
129:
130: public void setColumns(Column[] cols) {
131: if (cols == null)
132: cols = Schemas.EMPTY_COLUMNS;
133: _cols = cols;
134: }
135:
136: public ColumnIO getColumnIO() {
137: if (_cols.length == 0 && _fk == null
138: && getValueMappedBy() != null)
139: return getValueMappedByMapping().getColumnIO();
140: return (_io == null) ? ColumnIO.UNRESTRICTED : _io;
141: }
142:
143: public void setColumnIO(ColumnIO io) {
144: _io = io;
145: }
146:
147: public ForeignKey getForeignKey() {
148: if (_fk == null && getValueMappedBy() != null)
149: return getValueMappedByMapping().getForeignKey();
150: return _fk;
151: }
152:
153: public void setForeignKey(ForeignKey fk) {
154: _fk = fk;
155: if (fk == null)
156: _join = JOIN_FORWARD;
157: }
158:
159: public ForeignKey getForeignKey(ClassMapping target) {
160: if (_fk == null && getValueMappedBy() != null)
161: return getValueMappedByMapping().getForeignKey(target);
162: if (target == null)
163: return _fk;
164: if (_fk == null && _cols.length == 0)
165: return null;
166:
167: // always use least-derived joinable type
168: for (ClassMapping sup = target; sup != null; sup = sup
169: .getJoinablePCSuperclassMapping()) {
170: if (sup == getTypeMetaData())
171: return _fk;
172: target = sup;
173: }
174:
175: synchronized (this ) {
176: if (_targetFKs != null) {
177: Object cachedFK = _targetFKs.get(target);
178: if (cachedFK != null)
179: return (ForeignKey) cachedFK;
180: } else
181: _targetFKs = new HashMap();
182:
183: ForeignKey newfk = (_join == JOIN_FORWARD) ? newForwardForeignKey(target)
184: : newInverseForeignKey(target);
185: _targetFKs.put(target, newfk);
186: return newfk;
187: }
188: }
189:
190: /**
191: * Create a forward foreign key to the given target.
192: */
193: private ForeignKey newForwardForeignKey(ClassMapping target) {
194: Table table;
195: Column[] cols;
196: if (_fk == null) {
197: table = _cols[0].getTable();
198: cols = _cols;
199: } else {
200: table = _fk.getTable();
201: cols = _fk.getColumns();
202: }
203:
204: // gather target cols before adding foreign key to table in case
205: // there is an error while looking for a target col
206: Column[] tcols = new Column[cols.length];
207: for (int i = 0; i < cols.length; i++) {
208: if (cols[i].getTargetField() != null)
209: tcols[i] = getEquivalentColumn(cols[i], target, cols[i]
210: .getTargetField());
211: else if (_fk != null)
212: tcols[i] = getEquivalentColumn(_fk.getPrimaryKeyColumn(
213: cols[i]).getName(), target, true);
214: else if (cols[i].getTarget() != null)
215: tcols[i] = getEquivalentColumn(cols[i].getTarget(),
216: target, true);
217: else
218: tcols[i] = getEquivalentColumn(cols[i].getName(),
219: target, false);
220: }
221:
222: ForeignKey newfk = table.addForeignKey();
223: newfk.setJoins(cols, tcols);
224: if (_fk != null) {
225: cols = _fk.getConstantColumns();
226: for (int i = 0; i < cols.length; i++)
227: newfk.joinConstant(cols[i], _fk.getConstant(cols[i]));
228:
229: cols = _fk.getConstantPrimaryKeyColumns();
230: for (int i = 0; i < cols.length; i++)
231: newfk.joinConstant(_fk.getPrimaryKeyConstant(cols[i]),
232: getEquivalentColumn(cols[i].getName(), target,
233: true));
234: }
235: return newfk;
236: }
237:
238: /**
239: * Return the given mapping's equivalent to the given column, using the
240: * target field.
241: */
242: private Column getEquivalentColumn(Column col, ClassMapping target,
243: String fieldName) {
244: fieldName = fieldName.substring(fieldName.indexOf('.') + 1);
245: FieldMapping field = target.getFieldMapping(fieldName);
246: if (field == null)
247: throw new MetaDataException(_loc.get("no-equiv-field",
248: new Object[] { this , target, fieldName, col }));
249:
250: Column[] cols = field.getColumns();
251: if (cols.length != 1)
252: throw new MetaDataException(_loc.get("bad-equiv-field",
253: new Object[] { this , target, fieldName, col }));
254:
255: return cols[0];
256: }
257:
258: /**
259: * Return the given mapping's equivalent of the given column.
260: */
261: private Column getEquivalentColumn(String colName,
262: ClassMapping target, boolean explicit) {
263: // if there was no explicit target, use single pk column
264: if (!explicit) {
265: for (ClassMapping cls = target; cls != null; cls = cls
266: .getJoinablePCSuperclassMapping()) {
267: if (cls.getTable() != null) {
268: if (cls.getPrimaryKeyColumns().length == 1)
269: return cls.getPrimaryKeyColumns()[0];
270: break;
271: }
272: }
273: }
274:
275: Column ret;
276: for (ClassMapping cls = target; cls != null; cls = cls
277: .getJoinablePCSuperclassMapping()) {
278: if (cls.getTable() != null) {
279: ret = cls.getTable().getColumn(colName);
280: if (ret != null)
281: return ret;
282: }
283: }
284:
285: throw new MetaDataException(_loc.get("no-equiv-col", this ,
286: target, colName));
287: }
288:
289: /**
290: * Return an inverse foreign key from the given related type to our table.
291: */
292: private ForeignKey newInverseForeignKey(ClassMapping target) {
293: FieldMapping field = getFieldMapping();
294: FieldMapping mapped = field.getMappedByMapping();
295: if (mapped == null)
296: throw new MetaDataException(_loc.get("cant-inverse", this ));
297:
298: mapped = target.getFieldMapping(mapped.getIndex());
299: if (mapped == null || mapped.getTypeCode() != JavaTypes.PC)
300: throw new MetaDataException(_loc.get("no-equiv-mapped-by",
301: this , target, field.getMappedBy()));
302: return mapped.getForeignKey();
303: }
304:
305: public int getJoinDirection() {
306: if (_fk == null && getValueMappedBy() != null)
307: return getValueMappedByMapping().getJoinDirection();
308: return _join;
309: }
310:
311: public void setJoinDirection(int direction) {
312: _join = direction;
313: }
314:
315: public void setForeignKey(Row row, OpenJPAStateManager rel)
316: throws SQLException {
317: if (rel != null)
318: row.setForeignKey(getForeignKey((ClassMapping) rel
319: .getMetaData()), _io, rel);
320: else if (_fk != null)
321: row.setForeignKey(_fk, _io, null);
322: else {
323: for (int i = 0; i < _cols.length; i++) {
324: if (_io == null
325: || (row.getAction() == Row.ACTION_INSERT && _io
326: .isInsertable(i, true))
327: || (row.getAction() != Row.ACTION_INSERT && _io
328: .isUpdatable(i, true)))
329: row.setNull(_cols[i]);
330: }
331: }
332: }
333:
334: public void whereForeignKey(Row row, OpenJPAStateManager rel)
335: throws SQLException {
336: if (rel != null)
337: row.whereForeignKey(getForeignKey((ClassMapping) rel
338: .getMetaData()), rel);
339: else if (_fk != null)
340: row.whereForeignKey(_fk, null);
341: else
342: for (int i = 0; i < _cols.length; i++)
343: row.whereNull(_cols[i]);
344: }
345:
346: public ClassMapping[] getIndependentTypeMappings() {
347: ClassMapping rel = getTypeMapping();
348: if (rel == null)
349: return ClassMapping.EMPTY_MAPPINGS;
350: if (_poly != POLY_TRUE) {
351: if (!rel.isMapped())
352: return ClassMapping.EMPTY_MAPPINGS;
353: if (_typeArr == null)
354: _typeArr = new ClassMapping[] { rel };
355: return _typeArr;
356: }
357: return rel.getIndependentAssignableMappings();
358: }
359:
360: public int getSelectSubclasses() {
361: ClassMapping rel = getTypeMapping();
362: if (rel == null || !rel.isMapped())
363: return -1;
364:
365: switch (_poly) {
366: case POLY_FALSE:
367: return (_criteria) ? Select.SUBS_NONE : Select.SUBS_EXACT;
368: case POLY_TRUE:
369: ClassMapping[] assign = rel
370: .getIndependentAssignableMappings();
371: if (assign.length != 1 || assign[0] != rel)
372: return -1;
373: // no break
374: case POLY_JOINABLE:
375: return (_criteria) ? Select.SUBS_JOINABLE
376: : Select.SUBS_ANY_JOINABLE;
377: default:
378: throw new InternalException();
379: }
380: }
381:
382: public Unique getValueUnique() {
383: return _unq;
384: }
385:
386: public void setValueUnique(Unique unq) {
387: _unq = unq;
388: }
389:
390: public Index getValueIndex() {
391: return _idx;
392: }
393:
394: public void setValueIndex(Index idx) {
395: _idx = idx;
396: }
397:
398: public boolean getUseClassCriteria() {
399: if (_fk == null && getValueMappedBy() != null)
400: return getValueMappedByMapping().getUseClassCriteria();
401: return _criteria;
402: }
403:
404: public void setUseClassCriteria(boolean criteria) {
405: _criteria = criteria;
406: }
407:
408: public int getPolymorphic() {
409: return _poly;
410: }
411:
412: public void setPolymorphic(int poly) {
413: _poly = poly;
414: }
415:
416: public void refSchemaComponents() {
417: for (int i = 0; i < _cols.length; i++)
418: _cols[i].ref();
419: if (_fk != null) {
420: _fk.ref();
421: _fk.refColumns();
422: }
423:
424: ClassMapping embed = getEmbeddedMapping();
425: if (embed != null)
426: embed.refSchemaComponents();
427: }
428:
429: public void mapConstraints(String name, boolean adapt) {
430: _unq = _info.getUnique(this , name, adapt);
431: _idx = _info.getIndex(this , name, adapt);
432: }
433:
434: public void clearMapping() {
435: _handler = null;
436: _cols = Schemas.EMPTY_COLUMNS;
437: _unq = null;
438: _idx = null;
439: _fk = null;
440: _join = JOIN_FORWARD;
441: _info.clear();
442: setResolve(MODE_MAPPING | MODE_MAPPING_INIT, false);
443: }
444:
445: public void syncMappingInfo() {
446: if (getValueMappedBy() != null)
447: _info.clear();
448: else {
449: _info.syncWith(this );
450: ClassMapping embed = getEmbeddedMapping();
451: if (embed != null)
452: embed.syncMappingInfo();
453: }
454: }
455:
456: public void copyMappingInfo(ValueMapping vm) {
457: setValueMappedBy(vm.getValueMappedBy());
458: setPolymorphic(vm.getPolymorphic());
459: _info.copy(vm.getValueInfo());
460:
461: ClassMapping embed = vm.getEmbeddedMapping();
462: if (embed != null && getEmbeddedMapping() != null) {
463: FieldMapping[] tmplates = embed.getFieldMappings();
464: FieldMapping[] fms = getEmbeddedMapping()
465: .getFieldMappings();
466: if (tmplates.length == fms.length)
467: for (int i = 0; i < fms.length; i++)
468: fms[i].copyMappingInfo(tmplates[i]);
469: }
470: }
471:
472: public boolean resolve(int mode) {
473: int cur = getResolve();
474: if (super .resolve(mode))
475: return true;
476: ClassMapping embed = getEmbeddedMapping();
477: if (embed != null)
478: embed.resolve(mode);
479: if ((mode & MODE_MAPPING) != 0 && (cur & MODE_MAPPING) == 0)
480: resolveMapping();
481: if ((mode & MODE_MAPPING_INIT) != 0
482: && (cur & MODE_MAPPING_INIT) == 0)
483: initializeMapping();
484: return false;
485: }
486:
487: /**
488: * Setup mapping. Our handler will already have been set by our owning
489: * field.
490: */
491: private void resolveMapping() {
492: // mark mapped columns
493: Column[] cols;
494: int insertFlag;
495: if (_fk != null) {
496: cols = _fk.getColumns();
497: insertFlag = Column.FLAG_FK_INSERT;
498: } else {
499: cols = getColumns();
500: insertFlag = Column.FLAG_DIRECT_INSERT;
501: }
502: ColumnIO io = getColumnIO();
503: for (int i = 0; i < cols.length; i++) {
504: if (io.isInsertable(i, false))
505: cols[i].setFlag(insertFlag, true);
506: if (io.isUpdatable(i, false))
507: cols[i].setFlag(insertFlag, true);
508: }
509: }
510:
511: /**
512: * Prepare mapping for runtime use.
513: */
514: private void initializeMapping() {
515: if (_fk == null)
516: return;
517:
518: // if our fk cols are direct mapped by other values, make them
519: // non-nullable
520: Column[] cols = _fk.getColumns();
521: for (int i = 0; i < cols.length; i++) {
522: if (cols[i].getFlag(Column.FLAG_DIRECT_INSERT))
523: newIO().setNullInsertable(i, false);
524: if (cols[i].getFlag(Column.FLAG_DIRECT_UPDATE))
525: newIO().setNullUpdatable(i, false);
526: }
527:
528: // if anything maps our constant fk cols, make them read only
529: int len = cols.length;
530: cols = _fk.getConstantColumns();
531: for (int i = 0; i < cols.length; i++) {
532: if (cols[i].getFlag(Column.FLAG_DIRECT_INSERT)
533: || cols[i].getFlag(Column.FLAG_FK_INSERT))
534: newIO().setInsertable(len + i, false);
535: if (cols[i].getFlag(Column.FLAG_DIRECT_UPDATE)
536: || cols[i].getFlag(Column.FLAG_FK_UPDATE))
537: newIO().setUpdatable(len + i, false);
538: }
539: }
540:
541: /**
542: * Return the column I/O information, creating it if necessary.
543: */
544: private ColumnIO newIO() {
545: if (_io == null)
546: _io = new ColumnIO();
547: return _io;
548: }
549: }
|