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.schema;
020:
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.TreeSet;
029:
030: import org.apache.commons.lang.StringUtils;
031: import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
032: import org.apache.openjpa.lib.meta.XMLMetaDataSerializer;
033: import org.apache.openjpa.lib.util.Localizer;
034: import org.xml.sax.SAXException;
035:
036: /**
037: * Serializes {@link Schema}s to XML matching the document
038: * type definition defined by the {@link XMLSchemaParser}. The serializer
039: * actually works at the fine-grained table level to allow you to split
040: * schemas among multiple files.
041: * Serializers are not thread safe.
042: *
043: * @author Abe White
044: * @nojavadoc
045: */
046: public class XMLSchemaSerializer extends XMLMetaDataSerializer
047: implements SchemaSerializer {
048:
049: private static final Localizer _loc = Localizer
050: .forPackage(XMLSchemaSerializer.class);
051:
052: private final Collection _tables = new TreeSet();
053: private final Collection _seqs = new TreeSet();
054:
055: /**
056: * Constructor. Supply configuration.
057: */
058: public XMLSchemaSerializer(JDBCConfiguration conf) {
059: setLog(conf.getLog(JDBCConfiguration.LOG_SCHEMA));
060: }
061:
062: public Table[] getTables() {
063: return (Table[]) _tables.toArray(new Table[_tables.size()]);
064: }
065:
066: public void addTable(Table table) {
067: if (table != null)
068: _tables.add(table);
069: }
070:
071: public boolean removeTable(Table table) {
072: return _tables.remove(table);
073: }
074:
075: public Sequence[] getSequences() {
076: return (Sequence[]) _seqs.toArray(new Sequence[_seqs.size()]);
077: }
078:
079: public void addSequence(Sequence seq) {
080: if (seq != null)
081: _seqs.add(seq);
082: }
083:
084: public boolean removeSequence(Sequence seq) {
085: return _seqs.remove(seq);
086: }
087:
088: public void addAll(Schema schema) {
089: if (schema == null)
090: return;
091: Table[] tables = schema.getTables();
092: for (int i = 0; i < tables.length; i++)
093: addTable(tables[i]);
094: Sequence[] seqs = schema.getSequences();
095: for (int i = 0; i < seqs.length; i++)
096: addSequence(seqs[i]);
097: }
098:
099: public void addAll(SchemaGroup group) {
100: if (group == null)
101: return;
102: Schema[] schemas = group.getSchemas();
103: for (int i = 0; i < schemas.length; i++)
104: addAll(schemas[i]);
105: }
106:
107: public boolean removeAll(Schema schema) {
108: if (schema == null)
109: return false;
110:
111: boolean removed = false;
112: Table[] tables = schema.getTables();
113: for (int i = 0; i < tables.length; i++)
114: removed |= removeTable(tables[i]);
115: Sequence[] seqs = schema.getSequences();
116: for (int i = 0; i < seqs.length; i++)
117: removed |= removeSequence(seqs[i]);
118: return removed;
119: }
120:
121: public boolean removeAll(SchemaGroup group) {
122: if (group == null)
123: return false;
124:
125: boolean removed = false;
126: Schema[] schemas = group.getSchemas();
127: for (int i = 0; i < schemas.length; i++)
128: removed |= removeAll(schemas[i]);
129: return removed;
130: }
131:
132: public void clear() {
133: _tables.clear();
134: _seqs.clear();
135: }
136:
137: protected Collection getObjects() {
138: if (_seqs.isEmpty())
139: return _tables;
140: if (_tables.isEmpty())
141: return _seqs;
142: List all = new ArrayList(_seqs.size() + _tables.size());
143: all.addAll(_seqs);
144: all.addAll(_tables);
145: return all;
146: }
147:
148: protected void serialize(Collection objs) throws SAXException {
149: // group the objects by schema
150: Map schemas = new HashMap();
151: String schemaName;
152: Collection schemaObjs;
153: Object obj;
154: for (Iterator itr = objs.iterator(); itr.hasNext();) {
155: obj = itr.next();
156: if (obj instanceof Table)
157: schemaName = ((Table) obj).getSchemaName();
158: else
159: schemaName = ((Sequence) obj).getSchemaName();
160: schemaObjs = (Collection) schemas.get(schemaName);
161: if (schemaObjs == null) {
162: schemaObjs = new LinkedList();
163: schemas.put(schemaName, schemaObjs);
164: }
165: schemaObjs.add(obj);
166: }
167:
168: startElement("schemas");
169: Map.Entry entry;
170: for (Iterator itr = schemas.entrySet().iterator(); itr
171: .hasNext();) {
172: entry = (Map.Entry) itr.next();
173: serializeSchema((String) entry.getKey(), (Collection) entry
174: .getValue());
175: }
176: endElement("schemas");
177: }
178:
179: /**
180: * Serializes the given objects together into the current schema.
181: */
182: private void serializeSchema(String name, Collection objs)
183: throws SAXException {
184: if (objs.isEmpty())
185: return;
186:
187: if (getLog().isTraceEnabled())
188: getLog().trace(_loc.get("ser-schema", name));
189:
190: if (name != null)
191: addAttribute("name", name);
192: startElement("schema");
193:
194: // tables and seqs
195: Object obj;
196: for (Iterator itr = objs.iterator(); itr.hasNext();) {
197: obj = itr.next();
198: if (obj instanceof Table)
199: serializeTable((Table) obj);
200: else
201: serializeSequence((Sequence) obj);
202: }
203:
204: endElement("schema");
205: }
206:
207: /**
208: * Serialize the given sequence.
209: */
210: private void serializeSequence(Sequence seq) throws SAXException {
211: addAttribute("name", seq.getName());
212: if (seq.getInitialValue() != 1)
213: addAttribute("initial-value", String.valueOf(seq
214: .getInitialValue()));
215: if (seq.getIncrement() > 1)
216: addAttribute("increment", String
217: .valueOf(seq.getIncrement()));
218: if (seq.getAllocate() > 1)
219: addAttribute("allocate", String.valueOf(seq.getAllocate()));
220: startElement("sequence");
221: endElement("sequence");
222: }
223:
224: /**
225: * Serializes the given table.
226: */
227: private void serializeTable(Table table) throws SAXException {
228: addAttribute("name", table.getName());
229: startElement("table");
230:
231: // primary key
232: PrimaryKey pk = table.getPrimaryKey();
233: if (pk != null)
234: serializePrimaryKey(pk);
235:
236: // columns
237: Column[] cols = table.getColumns();
238: for (int i = 0; i < cols.length; i++)
239: serializeColumn(cols[i]);
240:
241: // foreign keys
242: ForeignKey[] fks = table.getForeignKeys();
243: for (int i = 0; i < fks.length; i++)
244: serializeForeignKey(fks[i]);
245:
246: // indexes
247: Index[] idxs = table.getIndexes();
248: for (int i = 0; i < idxs.length; i++)
249: serializeIndex(idxs[i]);
250:
251: // unique constraints
252: Unique[] unqs = table.getUniques();
253: for (int i = 0; i < unqs.length; i++)
254: serializeUnique(unqs[i]);
255:
256: endElement("table");
257: }
258:
259: /**
260: * Serializes the given column.
261: */
262: private void serializeColumn(Column col) throws SAXException {
263: addAttribute("name", col.getName());
264: addAttribute("type", Schemas.getJDBCName(col.getType()));
265: if (!StringUtils.isEmpty(col.getTypeName())
266: && !col.getTypeName().equalsIgnoreCase(
267: Schemas.getJDBCName(col.getType())))
268: addAttribute("type-name", col.getTypeName());
269: if (col.isNotNull())
270: addAttribute("not-null", "true");
271: if (col.isAutoAssigned())
272: addAttribute("auto-assign", "true");
273: if (col.getDefaultString() != null)
274: addAttribute("default", col.getDefaultString());
275: if (col.getSize() != 0)
276: addAttribute("size", String.valueOf(col.getSize()));
277: if (col.getDecimalDigits() != 0)
278: addAttribute("decimal-digits", String.valueOf(col
279: .getDecimalDigits()));
280: startElement("column");
281: endElement("column");
282: }
283:
284: /**
285: * Serializes the given primary key.
286: */
287: private void serializePrimaryKey(PrimaryKey pk) throws SAXException {
288: if (pk.getName() != null)
289: addAttribute("name", pk.getName());
290: if (pk.isLogical())
291: addAttribute("logical", "true");
292:
293: Column[] cols = pk.getColumns();
294: if (cols.length == 1)
295: addAttribute("column", cols[0].getName());
296: startElement("pk");
297:
298: // columns
299: if (cols.length > 1)
300: for (int i = 0; i < cols.length; i++)
301: serializeOn(cols[i]);
302:
303: endElement("pk");
304: }
305:
306: /**
307: * Serializes the given index.
308: */
309: private void serializeIndex(Index idx) throws SAXException {
310: addAttribute("name", idx.getName());
311: if (idx.isUnique())
312: addAttribute("unique", "true");
313: Column[] cols = idx.getColumns();
314: if (cols.length == 1)
315: addAttribute("column", cols[0].getName());
316: startElement("index");
317:
318: // columns
319: if (cols.length > 1)
320: for (int i = 0; i < cols.length; i++)
321: serializeOn(cols[i]);
322:
323: endElement("index");
324: }
325:
326: /**
327: * Serializes the given constraint.
328: */
329: private void serializeUnique(Unique unq) throws SAXException {
330: if (unq.getName() != null)
331: addAttribute("name", unq.getName());
332: if (unq.isDeferred())
333: addAttribute("deferred", "true");
334: Column[] cols = unq.getColumns();
335: if (cols.length == 1)
336: addAttribute("column", cols[0].getName());
337: startElement("unique");
338:
339: // columns
340: if (cols.length > 1)
341: for (int i = 0; i < cols.length; i++)
342: serializeOn(cols[i]);
343:
344: endElement("unique");
345: }
346:
347: /**
348: * Serializes the given foreign key.
349: */
350: private void serializeForeignKey(ForeignKey fk) throws SAXException {
351: if (fk.getName() != null)
352: addAttribute("name", fk.getName());
353:
354: if (fk.isDeferred())
355: addAttribute("deferred", "true");
356:
357: if (fk.getDeleteAction() != ForeignKey.ACTION_NONE)
358: addAttribute("delete-action", ForeignKey.getActionName(fk
359: .getDeleteAction()));
360: if (fk.getUpdateAction() != ForeignKey.ACTION_NONE
361: && fk.getUpdateAction() != ForeignKey.ACTION_RESTRICT)
362: addAttribute("update-action", ForeignKey.getActionName(fk
363: .getUpdateAction()));
364:
365: Column[] cols = fk.getColumns();
366: Column[] pks = fk.getPrimaryKeyColumns();
367: Column[] consts = fk.getConstantColumns();
368: Column[] constsPK = fk.getConstantPrimaryKeyColumns();
369: addAttribute("to-table", fk.getPrimaryKeyTable().getFullName());
370: if (cols.length == 1 && consts.length == 0
371: && constsPK.length == 0)
372: addAttribute("column", cols[0].getName());
373: startElement("fk");
374:
375: // columns
376: if (cols.length > 1 || consts.length > 0 || constsPK.length > 0)
377: for (int i = 0; i < cols.length; i++)
378: serializeJoin(cols[i], pks[i]);
379: for (int i = 0; i < consts.length; i++)
380: serializeJoin(consts[i], fk.getConstant(consts[i]));
381: for (int i = 0; i < constsPK.length; i++)
382: serializeJoin(fk.getPrimaryKeyConstant(constsPK[i]),
383: constsPK[i]);
384:
385: endElement("fk");
386: }
387:
388: /**
389: * Serializes the given column to an 'on' element.
390: */
391: private void serializeOn(Column col) throws SAXException {
392: addAttribute("column", col.getName());
393: startElement("on");
394: endElement("on");
395: }
396:
397: /**
398: * Serializes the given columns to a 'join' element.
399: */
400: private void serializeJoin(Column col, Column pk)
401: throws SAXException {
402: addAttribute("column", col.getName());
403: addAttribute("to-column", pk.getName());
404: startElement("join");
405: endElement("join");
406: }
407:
408: /**
409: * Serializes the given values to a 'join' element.
410: */
411: private void serializeJoin(Object val, Column pk)
412: throws SAXException {
413: addAttribute("value", stringifyConstant(val));
414: addAttribute("to-column", pk.getName());
415: startElement("join");
416: endElement("join");
417: }
418:
419: /**
420: * Serializes the given values to a 'join' element.
421: */
422: private void serializeJoin(Column col, Object val)
423: throws SAXException {
424: addAttribute("column", col.getName());
425: addAttribute("value", stringifyConstant(val));
426: startElement("join");
427: endElement("join");
428: }
429:
430: /**
431: * Stringify the given constant value.
432: */
433: private static String stringifyConstant(Object val) {
434: if (val == null)
435: return "null";
436: if (val instanceof String)
437: return "'" + val + "'";
438: return val.toString();
439: }
440: }
|