| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.jdbc.schema; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeSet; |
| |
| import org.apache.openjpa.jdbc.conf.JDBCConfiguration; |
| import org.apache.openjpa.lib.meta.XMLMetaDataSerializer; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.StringUtil; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Serializes {@link Schema}s to XML matching the document |
| * type definition defined by the {@link XMLSchemaParser}. The serializer |
| * actually works at the fine-grained table level to allow you to split |
| * schemas among multiple files. |
| * Serializers are not thread safe. |
| * |
| * @author Abe White |
| */ |
| public class XMLSchemaSerializer |
| extends XMLMetaDataSerializer |
| implements SchemaSerializer { |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (XMLSchemaSerializer.class); |
| |
| private final Collection<Table> _tables = new TreeSet<>(); |
| private final Collection<Sequence> _seqs = new TreeSet<>(); |
| |
| /** |
| * Constructor. Supply configuration. |
| */ |
| public XMLSchemaSerializer(JDBCConfiguration conf) { |
| setLog(conf.getLog(JDBCConfiguration.LOG_SCHEMA)); |
| } |
| |
| @Override |
| public Table[] getTables() { |
| return (Table[]) _tables.toArray(new Table[_tables.size()]); |
| } |
| |
| @Override |
| public void addTable(Table table) { |
| if (table != null) |
| _tables.add(table); |
| } |
| |
| @Override |
| public boolean removeTable(Table table) { |
| return _tables.remove(table); |
| } |
| |
| public Sequence[] getSequences() { |
| return (Sequence[]) _seqs.toArray(new Sequence[_seqs.size()]); |
| } |
| |
| public void addSequence(Sequence seq) { |
| if (seq != null) |
| _seqs.add(seq); |
| } |
| |
| public boolean removeSequence(Sequence seq) { |
| return _seqs.remove(seq); |
| } |
| |
| @Override |
| public void addAll(Schema schema) { |
| if (schema == null) |
| return; |
| Table[] tables = schema.getTables(); |
| for (Table table : tables) { |
| addTable(table); |
| } |
| Sequence[] seqs = schema.getSequences(); |
| for (Sequence seq : seqs) { |
| addSequence(seq); |
| } |
| } |
| |
| @Override |
| public void addAll(SchemaGroup group) { |
| if (group == null) |
| return; |
| Schema[] schemas = group.getSchemas(); |
| for (Schema schema : schemas) { |
| addAll(schema); |
| } |
| } |
| |
| @Override |
| public boolean removeAll(Schema schema) { |
| if (schema == null) |
| return false; |
| |
| boolean removed = false; |
| Table[] tables = schema.getTables(); |
| for (Table table : tables) { |
| removed |= removeTable(table); |
| } |
| Sequence[] seqs = schema.getSequences(); |
| for (Sequence seq : seqs) { |
| removed |= removeSequence(seq); |
| } |
| return removed; |
| } |
| |
| @Override |
| public boolean removeAll(SchemaGroup group) { |
| if (group == null) |
| return false; |
| |
| boolean removed = false; |
| Schema[] schemas = group.getSchemas(); |
| for (Schema schema : schemas) { |
| removed |= removeAll(schema); |
| } |
| return removed; |
| } |
| |
| @Override |
| public void clear() { |
| _tables.clear(); |
| _seqs.clear(); |
| } |
| |
| @Override |
| protected Collection getObjects() { |
| if (_seqs.isEmpty()) |
| return _tables; |
| if (_tables.isEmpty()) |
| return _seqs; |
| List<Object> all = new ArrayList<>(_seqs.size() + _tables.size()); |
| all.addAll(_seqs); |
| all.addAll(_tables); |
| return all; |
| } |
| |
| @Override |
| protected void serialize(Collection objs) |
| throws SAXException { |
| // group the objects by schema |
| Map schemas = new HashMap(); |
| String schemaName; |
| Collection schemaObjs; |
| Object obj; |
| for (Object value : objs) { |
| obj = value; |
| if (obj instanceof Table) |
| schemaName = ((Table) obj).getSchemaName(); |
| else |
| schemaName = ((Sequence) obj).getSchemaName(); |
| schemaObjs = (Collection) schemas.get(schemaName); |
| if (schemaObjs == null) { |
| schemaObjs = new LinkedList(); |
| schemas.put(schemaName, schemaObjs); |
| } |
| schemaObjs.add(obj); |
| } |
| |
| startElement("schemas"); |
| Map.Entry entry; |
| for (Object o : schemas.entrySet()) { |
| entry = (Map.Entry) o; |
| serializeSchema((String) entry.getKey(), (Collection) |
| entry.getValue()); |
| } |
| endElement("schemas"); |
| } |
| |
| /** |
| * Serializes the given objects together into the current schema. |
| */ |
| private void serializeSchema(String name, Collection<?> objs) |
| throws SAXException { |
| if (objs.isEmpty()) |
| return; |
| |
| if (getLog().isTraceEnabled()) |
| getLog().trace(_loc.get("ser-schema", name)); |
| |
| if (name != null) |
| addAttribute("name", name); |
| startElement("schema"); |
| |
| // tables and seqs |
| Object obj; |
| for (Object o : objs) { |
| obj = o; |
| if (obj instanceof Table) |
| serializeTable((Table) obj); |
| else |
| serializeSequence((Sequence) obj); |
| } |
| |
| endElement("schema"); |
| } |
| |
| /** |
| * Serialize the given sequence. |
| */ |
| private void serializeSequence(Sequence seq) |
| throws SAXException { |
| addAttribute("name", seq.getName()); |
| if (seq.getInitialValue() != 1) |
| addAttribute("initial-value", |
| String.valueOf(seq.getInitialValue())); |
| if (seq.getIncrement() > 1) |
| addAttribute("increment", String.valueOf(seq.getIncrement())); |
| if (seq.getAllocate() > 1) |
| addAttribute("allocate", String.valueOf(seq.getAllocate())); |
| startElement("sequence"); |
| endElement("sequence"); |
| } |
| |
| /** |
| * Serializes the given table. |
| */ |
| private void serializeTable(Table table) |
| throws SAXException { |
| addAttribute("name", table.getName()); |
| startElement("table"); |
| |
| // primary key |
| PrimaryKey pk = table.getPrimaryKey(); |
| if (pk != null) |
| serializePrimaryKey(pk); |
| |
| // columns |
| Column[] cols = table.getColumns(); |
| for (Column col : cols) { |
| serializeColumn(col); |
| } |
| |
| // foreign keys |
| ForeignKey[] fks = table.getForeignKeys(); |
| for (ForeignKey fk : fks) { |
| serializeForeignKey(fk); |
| } |
| |
| // indexes |
| Index[] idxs = table.getIndexes(); |
| for (Index idx : idxs) { |
| serializeIndex(idx); |
| } |
| |
| // unique constraints |
| Unique[] unqs = table.getUniques(); |
| for (Unique unq : unqs) { |
| serializeUnique(unq); |
| } |
| |
| endElement("table"); |
| } |
| |
| /** |
| * Serializes the given column. |
| */ |
| private void serializeColumn(Column col) |
| throws SAXException { |
| addAttribute("name", col.getName()); |
| addAttribute("type", Schemas.getJDBCName(col.getType())); |
| if (!StringUtil.isEmpty(col.getTypeName()) |
| && !col.getTypeName().equalsIgnoreCase |
| (Schemas.getJDBCName(col.getType()))) |
| addAttribute("type-name", col.getTypeName()); |
| if (col.isNotNull()) |
| addAttribute("not-null", "true"); |
| if (col.isAutoAssigned()) |
| addAttribute("auto-assign", "true"); |
| if (col.getDefaultString() != null) |
| addAttribute("default", col.getDefaultString()); |
| if (col.getSize() != 0) |
| addAttribute("size", String.valueOf(col.getSize())); |
| if (col.getDecimalDigits() != 0) |
| addAttribute("decimal-digits", String.valueOf |
| (col.getDecimalDigits())); |
| startElement("column"); |
| endElement("column"); |
| } |
| |
| /** |
| * Serializes the given primary key. |
| */ |
| private void serializePrimaryKey(PrimaryKey pk) |
| throws SAXException { |
| if (pk.getName() != null) |
| addAttribute("name", pk.getName()); |
| if (pk.isLogical()) |
| addAttribute("logical", "true"); |
| |
| Column[] cols = pk.getColumns(); |
| if (cols.length == 1) |
| addAttribute("column", cols[0].getName()); |
| startElement("pk"); |
| |
| // columns |
| if (cols.length > 1) |
| for (Column col : cols) { |
| serializeOn(col); |
| } |
| |
| endElement("pk"); |
| } |
| |
| /** |
| * Serializes the given index. |
| */ |
| private void serializeIndex(Index idx) |
| throws SAXException { |
| addAttribute("name", idx.getName()); |
| if (idx.isUnique()) |
| addAttribute("unique", "true"); |
| Column[] cols = idx.getColumns(); |
| if (cols.length == 1) |
| addAttribute("column", cols[0].getName()); |
| startElement("index"); |
| |
| // columns |
| if (cols.length > 1) |
| for (Column col : cols) { |
| serializeOn(col); |
| } |
| |
| endElement("index"); |
| } |
| |
| /** |
| * Serializes the given constraint. |
| */ |
| private void serializeUnique(Unique unq) |
| throws SAXException { |
| if (unq.getName() != null) |
| addAttribute("name", unq.getName()); |
| if (unq.isDeferred()) |
| addAttribute("deferred", "true"); |
| Column[] cols = unq.getColumns(); |
| if (cols.length == 1) |
| addAttribute("column", cols[0].getName()); |
| startElement("unique"); |
| |
| // columns |
| if (cols.length > 1) |
| for (Column col : cols) { |
| serializeOn(col); |
| } |
| |
| endElement("unique"); |
| } |
| |
| /** |
| * Serializes the given foreign key. |
| */ |
| private void serializeForeignKey(ForeignKey fk) |
| throws SAXException { |
| if (fk.getName() != null) |
| addAttribute("name", fk.getName()); |
| |
| if (fk.isDeferred()) |
| addAttribute("deferred", "true"); |
| |
| if (fk.getDeleteAction() != ForeignKey.ACTION_NONE) |
| addAttribute("delete-action", ForeignKey.getActionName |
| (fk.getDeleteAction())); |
| if (fk.getUpdateAction() != ForeignKey.ACTION_NONE |
| && fk.getUpdateAction() != ForeignKey.ACTION_RESTRICT) |
| addAttribute("update-action", ForeignKey.getActionName |
| (fk.getUpdateAction())); |
| |
| Column[] cols = fk.getColumns(); |
| Column[] pks = fk.getPrimaryKeyColumns(); |
| Column[] consts = fk.getConstantColumns(); |
| Column[] constsPK = fk.getConstantPrimaryKeyColumns(); |
| addAttribute("to-table", fk.getPrimaryKeyTable().getFullName()); |
| if (cols.length == 1 && consts.length == 0 && constsPK.length == 0) |
| addAttribute("column", cols[0].getName()); |
| startElement("fk"); |
| |
| // columns |
| if (cols.length > 1 || consts.length > 0 || constsPK.length > 0) |
| for (int i = 0; i < cols.length; i++) |
| serializeJoin(cols[i], pks[i]); |
| for (Column aConst : consts) { |
| serializeJoin(aConst, fk.getConstant(aConst)); |
| } |
| for (Column column : constsPK) { |
| serializeJoin(fk.getPrimaryKeyConstant(column), column); |
| } |
| |
| endElement("fk"); |
| } |
| |
| /** |
| * Serializes the given column to an 'on' element. |
| */ |
| private void serializeOn(Column col) |
| throws SAXException { |
| addAttribute("column", col.getName()); |
| startElement("on"); |
| endElement("on"); |
| } |
| |
| /** |
| * Serializes the given columns to a 'join' element. |
| */ |
| private void serializeJoin(Column col, Column pk) |
| throws SAXException { |
| addAttribute("column", col.getName()); |
| addAttribute("to-column", pk.getName()); |
| startElement("join"); |
| endElement("join"); |
| } |
| |
| /** |
| * Serializes the given values to a 'join' element. |
| */ |
| private void serializeJoin(Object val, Column pk) |
| throws SAXException { |
| addAttribute("value", stringifyConstant(val)); |
| addAttribute("to-column", pk.getName()); |
| startElement("join"); |
| endElement("join"); |
| } |
| |
| /** |
| * Serializes the given values to a 'join' element. |
| */ |
| private void serializeJoin(Column col, Object val) |
| throws SAXException { |
| addAttribute("column", col.getName()); |
| addAttribute("value", stringifyConstant(val)); |
| startElement("join"); |
| endElement("join"); |
| } |
| |
| /** |
| * Stringify the given constant value. |
| */ |
| private static String stringifyConstant(Object val) { |
| if (val == null) |
| return "null"; |
| if (val instanceof String) |
| return "'" + val + "'"; |
| return val.toString(); |
| } |
| } |