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.util.List;
022:
023: import org.apache.openjpa.jdbc.schema.Column;
024: import org.apache.openjpa.jdbc.schema.ColumnIO;
025: import org.apache.openjpa.jdbc.schema.ForeignKey;
026: import org.apache.openjpa.jdbc.schema.Index;
027: import org.apache.openjpa.jdbc.schema.SchemaGroup;
028: import org.apache.openjpa.jdbc.schema.Table;
029: import org.apache.openjpa.jdbc.schema.Unique;
030: import org.apache.openjpa.lib.util.Localizer;
031: import org.apache.openjpa.meta.JavaTypes;
032: import org.apache.openjpa.util.MetaDataException;
033:
034: /**
035: * Information about the mapping from a field value to the schema, in
036: * raw form. The columns and tables used in mapping info will not be part of
037: * the {@link SchemaGroup} used at runtime. Rather, they will be structs
038: * with the relevant pieces of information filled in.
039: *
040: * @author Abe White
041: */
042: public class ValueMappingInfo extends MappingInfo {
043:
044: private static final Localizer _loc = Localizer
045: .forPackage(ValueMappingInfo.class);
046:
047: private boolean _criteria = false;
048: private boolean _canNull = true;
049:
050: /**
051: * Whether to use class criteria when joining to related type.
052: */
053: public boolean getUseClassCriteria() {
054: return _criteria;
055: }
056:
057: /**
058: * Whether to use class criteria when joining to related type.
059: */
060: public void setUseClassCriteria(boolean criteria) {
061: _criteria = criteria;
062: }
063:
064: /**
065: * Whether user has explicitly turned null indicator column off.
066: */
067: public boolean canIndicateNull() {
068: return _canNull;
069: }
070:
071: /**
072: * Whether user has explicitly turned null indicator column off.
073: */
074: public void setCanIndicateNull(boolean ind) {
075: _canNull = ind;
076: }
077:
078: /**
079: * Return the join from this value to its related type.
080: *
081: * @param name base name for value mapping
082: * @param inversable whether an inverse join is allowed
083: */
084: public ForeignKey getTypeJoin(final ValueMapping val,
085: final String name, boolean inversable, boolean adapt) {
086: ClassMapping rel = val.getTypeMapping();
087: if (rel == null)
088: return null;
089:
090: ForeignKeyDefaults def = new ForeignKeyDefaults() {
091: public ForeignKey get(Table local, Table foreign,
092: boolean inverse) {
093: return val.getMappingRepository().getMappingDefaults()
094: .getForeignKey(val, name, local, foreign,
095: inverse);
096: }
097:
098: public void populate(Table local, Table foreign,
099: Column col, Object target, boolean inverse,
100: int pos, int cols) {
101: val.getMappingRepository().getMappingDefaults()
102: .populateForeignKeyColumn(val, name, local,
103: foreign, col, target, inverse, pos,
104: cols);
105: }
106: };
107: return createForeignKey(val, null, getColumns(), def, val
108: .getFieldMapping().getTable(), val.getFieldMapping()
109: .getDefiningMapping(), rel, inversable, adapt);
110: }
111:
112: /**
113: * Return the join from the related type to this value.
114: */
115: public ForeignKey getInverseTypeJoin(final ValueMapping val,
116: final String name, boolean adapt) {
117: ClassMapping rel = val.getTypeMapping();
118: if (rel == null || rel.getTable() == null)
119: return null;
120:
121: ForeignKeyDefaults def = new ForeignKeyDefaults() {
122: public ForeignKey get(Table local, Table foreign,
123: boolean inverse) {
124: return val.getMappingRepository().getMappingDefaults()
125: .getForeignKey(val, name, local, foreign,
126: !inverse);
127: }
128:
129: public void populate(Table local, Table foreign,
130: Column col, Object target, boolean inverse,
131: int pos, int cols) {
132: val.getMappingRepository().getMappingDefaults()
133: .populateForeignKeyColumn(val, name, local,
134: foreign, col, target, !inverse, pos,
135: cols);
136: }
137: };
138: return createForeignKey(val, null, getColumns(), def, rel
139: .getTable(), rel, val.getFieldMapping()
140: .getDefiningMapping(), false, adapt);
141: }
142:
143: /**
144: * Return the columns for this value, based on the given templates.
145: */
146: public Column[] getColumns(ValueMapping val, String name,
147: Column[] tmplates, Table table, boolean adapt) {
148: orderColumnsByTargetField(val, tmplates, adapt);
149: val.getMappingRepository().getMappingDefaults()
150: .populateColumns(val, name, table, tmplates);
151: return createColumns(val, null, tmplates, table, adapt);
152: }
153:
154: /**
155: * Make given columns match up with the target fields supplied on the
156: * templates.
157: */
158: private void orderColumnsByTargetField(ValueMapping val,
159: Column[] tmplates, boolean adapt) {
160: if (tmplates.length < 2 || tmplates[0].getTargetField() == null)
161: return;
162: List cols = getColumns();
163: if (cols.isEmpty() || cols.size() != tmplates.length)
164: return;
165:
166: int pos = 0;
167: Column cur = (Column) cols.get(0);
168: Column next;
169: for (int i = 0; i < cols.size(); i++) {
170: if (cur.getTargetField() == null)
171: throw new MetaDataException(_loc.get("no-targetfield",
172: val));
173:
174: pos = findTargetField(tmplates, cur.getTargetField());
175: if (pos == -1)
176: throw new MetaDataException(_loc.get("bad-targetfield",
177: val, cur.getTargetField()));
178:
179: next = (Column) cols.get(pos);
180: cols.set(pos, cur);
181: cur = next;
182: }
183: }
184:
185: /**
186: * Return the position of the template column with the given target field.
187: */
188: public int findTargetField(Column[] tmplates, String target) {
189: for (int i = 0; i < tmplates.length; i++)
190: if (target.equals(tmplates[i].getTargetField()))
191: return i;
192: return -1;
193: }
194:
195: /**
196: * Return a unique constraint for the given columns, or null if none.
197: */
198: public Unique getUnique(ValueMapping val, String name, boolean adapt) {
199: Column[] cols = val.getColumns();
200: if (cols.length == 0)
201: return null;
202:
203: Unique unq = val.getMappingRepository().getMappingDefaults()
204: .getUnique(val, name, cols[0].getTable(), cols);
205: return createUnique(val, null, unq, cols, adapt);
206: }
207:
208: /**
209: * Return an index for the given columns, or null if none.
210: */
211: public Index getIndex(ValueMapping val, String name, boolean adapt) {
212: Column[] cols = val.getColumns();
213: if (cols.length == 0)
214: return null;
215:
216: Index idx = val.getMappingRepository().getMappingDefaults()
217: .getIndex(val, name, cols[0].getTable(), cols);
218: return createIndex(val, null, idx, cols, adapt);
219: }
220:
221: /**
222: * Return the null indicator column for this value, or null if none.
223: */
224: public Column getNullIndicatorColumn(ValueMapping val, String name,
225: Table table, boolean adapt) {
226: // reset IO
227: setColumnIO(null);
228:
229: // has the user explicitly turned null indicator off?
230: if (!_canNull)
231: return null;
232:
233: // extract given null-ind column
234: List cols = getColumns();
235: Column given = (cols.isEmpty()) ? null : (Column) cols.get(0);
236: MappingDefaults def = val.getMappingRepository()
237: .getMappingDefaults();
238: if (given == null && (!adapt && !def.defaultMissingInfo()))
239: return null;
240:
241: Column tmplate = new Column();
242: tmplate.setName(name + "_null");
243: tmplate.setJavaType(JavaTypes.INT);
244: if (!def.populateNullIndicatorColumns(val, name, table,
245: new Column[] { tmplate })
246: && given == null)
247: return null;
248:
249: if (given != null
250: && (given.getFlag(Column.FLAG_UNINSERTABLE) || given
251: .getFlag(Column.FLAG_UNUPDATABLE))) {
252: ColumnIO io = new ColumnIO();
253: io.setInsertable(0, !given
254: .getFlag(Column.FLAG_UNINSERTABLE));
255: io.setUpdatable(0, !given.getFlag(Column.FLAG_UNUPDATABLE));
256: setColumnIO(io);
257: }
258:
259: if (given != null && given.getName() != null) {
260: // test if given column name is actually a field name, in which
261: // case we use its column as the null indicator column
262: ClassMapping embed = val.getEmbeddedMapping();
263: FieldMapping efm = (embed == null) ? null : embed
264: .getFieldMapping(given.getName());
265: if (efm != null && efm.getColumns().length > 0)
266: given.setName(efm.getColumns()[0].getName());
267: }
268: boolean compat = given == null || given.getName() == null
269: || table == null || !table.isNameTaken(given.getName());
270:
271: return mergeColumn(val, "null-ind", tmplate, compat, given,
272: table, adapt, def.defaultMissingInfo());
273: }
274:
275: /**
276: * Synchronize internal information with the mapping data for the given
277: * value.
278: */
279: public void syncWith(ValueMapping val) {
280: clear(false);
281:
282: _criteria = val.getUseClassCriteria();
283: setColumnIO(val.getColumnIO());
284: if (val.getForeignKey() != null && val.getTypeMapping() != null
285: && val.getTypeMapping().getTable() != null) {
286: FieldMapping fm = val.getFieldMapping();
287: Table local = (fm.getJoinForeignKey() != null) ? fm
288: .getTable() : fm.getDefiningMapping().getTable();
289: Table foreign;
290: if (val.getJoinDirection() == ValueMapping.JOIN_EXPECTED_INVERSE) {
291: foreign = local;
292: local = val.getTypeMapping().getTable();
293: setJoinDirection(JOIN_FORWARD);
294: } else {
295: foreign = val.getTypeMapping().getTable();
296: setJoinDirection((val.getJoinDirection() == val.JOIN_FORWARD) ? JOIN_FORWARD
297: : JOIN_INVERSE);
298: }
299: syncForeignKey(val, val.getForeignKey(), local, foreign);
300: } else
301: syncColumns(val, val.getColumns(), false);
302:
303: syncIndex(val, val.getValueIndex());
304: syncUnique(val, val.getValueUnique());
305:
306: // explicit handler strategy if the handler isn't the expected default
307: if (val.getHandler() != null) {
308: ValueHandler def = val.getFieldMapping()
309: .getMappingRepository().defaultHandler(val);
310: if (def == null
311: || val.getHandler().getClass() != def.getClass())
312: setStrategy(val.getHandler().getClass().getName());
313: }
314: }
315:
316: protected void clear(boolean canFlags) {
317: super .clear(canFlags);
318: if (canFlags) {
319: _criteria = false;
320: _canNull = true;
321: }
322: }
323:
324: public void copy(MappingInfo info) {
325: super .copy(info);
326: if (!(info instanceof ValueMappingInfo))
327: return;
328:
329: ValueMappingInfo vinfo = (ValueMappingInfo) info;
330: if (!_criteria)
331: _criteria = vinfo.getUseClassCriteria();
332: if (_canNull)
333: _canNull = vinfo.canIndicateNull();
334: }
335: }
|