/* Copyright (C) 2006 EBI This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the itmplied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.biomart.builder.controller; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.biomart.builder.exceptions.PartitionException; import org.biomart.builder.model.Column; import org.biomart.builder.model.ComponentStatus; import org.biomart.builder.model.DataSet; import org.biomart.builder.model.Key; import org.biomart.builder.model.Mart; import org.biomart.builder.model.PartitionTable; import org.biomart.builder.model.Relation; import org.biomart.builder.model.Schema; import org.biomart.builder.model.Table; import org.biomart.builder.model.DataSet.DataSetColumn; import org.biomart.builder.model.DataSet.DataSetOptimiserType; import org.biomart.builder.model.DataSet.DataSetTable; import org.biomart.builder.model.DataSet.DataSetTableType; import org.biomart.builder.model.DataSet.ExpressionColumnDefinition; import org.biomart.builder.model.DataSet.SplitOptimiserColumnDef; import org.biomart.builder.model.DataSet.DataSetColumn.ExpressionColumn; import org.biomart.builder.model.Key.ForeignKey; import org.biomart.builder.model.Key.PrimaryKey; import org.biomart.builder.model.PartitionTable.PartitionColumn; import org.biomart.builder.model.PartitionTable.PartitionTableApplication; import org.biomart.builder.model.PartitionTable.PartitionTableApplication.PartitionAppliedRow; import org.biomart.builder.model.Relation.Cardinality; import org.biomart.builder.model.Relation.CompoundRelationDefinition; import org.biomart.builder.model.Relation.RestrictedRelationDefinition; import org.biomart.builder.model.Relation.UnrolledRelationDefinition; import org.biomart.builder.model.Schema.JDBCSchema; import org.biomart.builder.model.Table.RestrictedTableDefinition; import org.biomart.common.exceptions.AssociationException; import org.biomart.common.exceptions.DataModelException; import org.biomart.common.resources.Log; import org.biomart.common.resources.Resources; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * The MartBuilderXML class provides two static methods which serialize and * deserialize {@link Mart} objects to/from a basic XML format. A third method * saves a human-readable report based on the XML. * <p> * Writing is done by building up a map of objects to unique IDs. Where objects * cross-reference each other, they look up the unique ID in the map and * reference that instead. When reading, the reverse map is built up to achieve * the same effect. This system relies on objects being written out before they * are cross-referenced by other objects, so circular references are not * possible, and the file structure must be carefully planned to avoid other * situations where this may arise. * <p> * NOTE: The XML is version-specific. A formal DTD will be included with each * official release of MartBuilder. This DTD will be found in the * <tt>org.biomart.builder.resources</tt> package. * * @author Richard Holland <holland@ebi.ac.uk> * @version $Revision: 1.147 $, $Date: 2008-03-12 14:22:37 $, modified by * $Author: rh4 $ * @since 0.5 */ public class MartBuilderXML extends DefaultHandler { private static final String CURRENT_DTD_VERSION = "0.7"; private static final String[] SUPPORTED_DTD_VERSIONS = new String[] { "0.7", "0.6", "0.5" }; private static final String DTD_PUBLIC_ID_START = "-//EBI//DTD MartBuilder "; private static final String DTD_PUBLIC_ID_END = "//EN"; private static final String DTD_URL_START = "http://www.biomart.org/DTD/MartBuilder-"; private static final String DTD_URL_END = ".dtd"; private static String currentReadingDTDVersion; /** * The load method takes a {@link File} and loads up a {@link Mart} object * based on the XML contents of the file. This XML is usually generated by * the {@link MartBuilderXML#save(Mart,File)} method. * * @param file * the {@link File} to load the data from. * @return a {@link Mart} object containing the data from the file. * @throws IOException * if there was any problem reading the file. * @throws DataModelException * if the content of the file is not valid {@link Mart} XML, or * has any logical problems. */ public static Mart load(final File file) throws IOException, DataModelException { Log.info("Loading XML from " + file.getPath()); // Use the default (non-validating) parser final SAXParserFactory factory = SAXParserFactory.newInstance(); // Parse the input final MartBuilderXML loader = new MartBuilderXML(); try { final SAXParser saxParser = factory.newSAXParser(); saxParser.parse(file, loader); } catch (final ParserConfigurationException e) { throw new DataModelException(Resources.get("XMLConfigFailed"), e); } catch (final SAXException e) { throw new DataModelException(Resources.get("XMLUnparseable"), e); } // Get the constructed object. final Mart mart = loader.getConstructedMart(); // Check that we got something useful. if (mart == null) throw new DataModelException(Resources.get("fileNotSchemaVersion", MartBuilderXML.CURRENT_DTD_VERSION)); // Return. Log.info("Done loading XML from " + file.getPath()); return mart; } /** * The save method takes a {@link Mart} object and writes out XML describing * it to the given {@link File}. This XML can be read by the * {@link MartBuilderXML#load(File)} method. * * @param mart * {@link Mart} object containing the data for the file. * @param file * the {@link File} to save the data to. * @throws IOException * if there was any problem writing the file. * @throws DataModelException * if it encounters an object not writable under the current * DTD. * @throws PartitionException * if any partition stuff broke. */ public static void save(final Mart mart, final File file) throws IOException, DataModelException, PartitionException { Log.info("Saving XML as " + file.getPath()); // Open the file. final FileWriter fw = new FileWriter(file); try { // Write it out. (new MartBuilderXML()).writeXML(mart, fw, true); } catch (final IOException e) { throw e; } catch (final DataModelException e) { throw e; } finally { // Close the output stream. fw.close(); } Log.info("Done saving XML as " + file.getPath()); } private Mart constructedMart; private int currentElementID; private String currentOutputElement; private int currentOutputIndent; private Map mappedObjects; private Stack objectStack; private Map reverseMappedObjects; /** * This class is intended to be used only in a static context. It creates * its own instances internally as required. */ private MartBuilderXML() { this.constructedMart = null; this.currentOutputElement = null; this.currentOutputIndent = 0; this.currentElementID = 1; } private Mart getConstructedMart() { return this.constructedMart; } /** * Internal method which closes a tag in the output stream. * * @param name * the tag to close. * @param xmlWriter * the writer we are writing to. * @throws IOException * if it failed to write it. */ private void closeElement(final String name, final Writer xmlWriter) throws IOException { // Can we use the simple /> method? if (this.currentOutputElement != null && name.equals(this.currentOutputElement)) { // Yes, so put closing angle bracket and newline on it. xmlWriter.write("/>"); xmlWriter.write(System.getProperty("line.separator")); } else { // No, so use the full technique. // Decrease the indent. this.currentOutputIndent--; // Output any indent required. for (int i = this.currentOutputIndent; i > 0; i--) xmlWriter.write("\t"); // Output the tag. xmlWriter.write("</"); xmlWriter.write(name); xmlWriter.write(">\n"); } // Reset the current tag. this.currentOutputElement = null; } /** * Internal method which opens a tag in the output stream. * * @param name * the tag to open. * @param xmlWriter * the writer we are writing to. * @throws IOException * if it failed to write it. */ private void openElement(final String name, final Writer xmlWriter) throws IOException { // Are we already partway through one? if (this.currentOutputElement != null) { // Yes, so put closing angle bracket and newline on it. xmlWriter.write(">"); xmlWriter.write(System.getProperty("line.separator")); // Increase the indent. this.currentOutputIndent++; } // Write any indent required. for (int i = this.currentOutputIndent; i > 0; i--) xmlWriter.write("\t"); // Open the tag. xmlWriter.write("<"); // Write the tag. xmlWriter.write(name); // Update tag that we are currently writing. this.currentOutputElement = name; } /** * Internal method which writes an attribute in the output stream. * * @param name * the name of the attribute. * @param value * the value of the attribute. * @param xmlWriter * the writer we are writing to. * @throws IOException * if it failed to write it. */ private void writeAttribute(final String name, final String value, final Writer xmlWriter) throws IOException { // Write it. if (value == null || "".equals(value)) return; xmlWriter.write(" "); xmlWriter.write(name); xmlWriter.write("=\""); xmlWriter.write(value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">")); xmlWriter.write("\""); } /** * Internal method which writes a comma-separated list of attributes in the * output stream. * * @param name * the name of the attribute. * @param values * the values of the attribute. * @param xmlWriter * the writer we are writing to. * @throws IOException * if it failed to write it. */ private void writeListAttribute(final String name, final Object[] values, final Writer xmlWriter) throws IOException { // Write it. final StringBuffer sb = new StringBuffer(); for (int i = 0; i < values.length; i++) { final String value = values[i] == null ? null : values[i] .toString(); if (i > 0) sb.append(","); if (value != null) sb.append(value.replaceAll(",", "__COMMA__")); } this.writeAttribute(name, sb.length() == 0 ? null : sb.toString(), xmlWriter); } private String[] readListAttribute(final String string, final boolean blankIsSingleNull) { if (string == null || string.length() == 0) return blankIsSingleNull ? new String[] { null } : new String[0]; final String[] values = string.split("\\s*,\\s*", -1); for (int i = 0; i < values.length; i++) { values[i] = values[i].replaceAll("__COMMA__", ","); if (values[i].length() == 0) values[i] = null; } return values; } /** * Internal method which writes out a set of relations. * * @param relations * the set of {@link Relation}s to write. * @param xmlWriter * the writer to write to. * @throws IOException * if there was a problem writing to file. */ private void writeRelations(final Collection relations, final boolean writeExternal, final Writer xmlWriter) throws IOException { // Write out each relation in turn. for (final Iterator i = relations.iterator(); i.hasNext();) { final Relation r = (Relation) i.next(); if (writeExternal != r.isExternal()) continue; Log.debug("Writing relation: " + r); // Assign the relation an ID. final String relMappedID = "" + this.currentElementID++; this.reverseMappedObjects.put(r, relMappedID); // Write the relation. this.openElement("relation", xmlWriter); this.writeAttribute("id", relMappedID, xmlWriter); this.writeAttribute("cardinality", r.getCardinality().getName(), xmlWriter); this.writeAttribute("originalCardinality", r .getOriginalCardinality().getName(), xmlWriter); this.writeAttribute("firstKeyId", (String) this.reverseMappedObjects.get(r.getFirstKey()), xmlWriter); this.writeAttribute("secondKeyId", (String) this.reverseMappedObjects.get(r.getSecondKey()), xmlWriter); this.writeAttribute("status", r.getStatus().toString(), xmlWriter); this.writeAttribute("visibleModified", Boolean.toString(r .isVisibleModified()), xmlWriter); this.closeElement("relation", xmlWriter); } } /** * Internal method which writes an entire schema out to file. * * @param schema * the schema to write. * @param xmlWriter * the writer to write to. * @throws IOException * in case there was any problem writing the file. * @throws DataModelException * if there were any logical problems with the schema. */ private void writeSchema(final Schema schema, final Writer xmlWriter) throws IOException, DataModelException { Log.debug("Writing schema: " + schema); // What kind of schema is it? if (schema instanceof JDBCSchema) { Log.debug("Writing JDBC schema"); // It's a JDBC schema. final JDBCSchema jdbcSchema = (JDBCSchema) schema; // Begin the schema element. this.openElement("jdbcSchema", xmlWriter); this.writeAttribute("uniqueId", "" + jdbcSchema.getUniqueId(), xmlWriter); this.writeAttribute("driverClassName", jdbcSchema .getDriverClassName(), xmlWriter); this.writeAttribute("url", jdbcSchema.getUrl(), xmlWriter); this.writeAttribute("databaseName", jdbcSchema .getDataLinkDatabase(), xmlWriter); this.writeAttribute("schemaName", jdbcSchema.getDataLinkSchema(), xmlWriter); this .writeAttribute("username", jdbcSchema.getUsername(), xmlWriter); if (jdbcSchema.getPassword() != null) this.writeAttribute("password", jdbcSchema.getPassword(), xmlWriter); this.writeAttribute("name", jdbcSchema.getName(), xmlWriter); this.writeAttribute("keyguessing", Boolean.toString(jdbcSchema .isKeyGuessing()), xmlWriter); this.writeAttribute("masked", Boolean.toString(jdbcSchema .isMasked()), xmlWriter); this.writeAttribute("hideMasked", Boolean.toString(jdbcSchema .isHideMasked()), xmlWriter); // Partitions. this.writeAttribute("partitionRegex", jdbcSchema .getPartitionRegex(), xmlWriter); this.writeAttribute("partitionExpression", jdbcSchema .getPartitionNameExpression(), xmlWriter); } // Other schema types are not recognised. else throw new DataModelException(Resources.get("unknownSchemaType", schema.getClass().getName())); // Write out the contents. this.writeSchemaContents(schema, xmlWriter); // Close the schema element. // What kind of schema was it? // JDBC? if (schema instanceof JDBCSchema) this.closeElement("jdbcSchema", xmlWriter); // Others? else throw new DataModelException(Resources.get("unknownSchemaType", schema.getClass().getName())); } /** * Internal method which writes out the contents of a schema. * * @param schema * the {@link Schema} to write out the tables of. * @param xmlWriter * the writer to write to. * @throws IOException * if there was a problem writing to file. * @throws AssociationException * if an unwritable kind of object was found. */ private void writeSchemaContents(final Schema schema, final Writer xmlWriter) throws IOException, DataModelException { Log.debug("Writing schema contents for " + schema); // Write out tables inside each schema. for (final Iterator ti = schema.getTables().values().iterator(); ti .hasNext();) { final Table table = (Table) ti.next(); Log.debug("Writing table: " + table); // Give the table an ID. final String tableMappedID = "" + this.currentElementID++; this.reverseMappedObjects.put(table, tableMappedID); // Start table. this.openElement("table", xmlWriter); this .writeAttribute("uniqueId", "" + table.getUniqueId(), xmlWriter); this.writeAttribute("id", tableMappedID, xmlWriter); this.writeAttribute("name", table.getName(), xmlWriter); this.writeAttribute("ignore", Boolean.toString(table.isMasked()), xmlWriter); this.writeListAttribute("inSchemaPartition", table .getSchemaPartitions().toArray(), xmlWriter); // Write out columns inside each table. for (final Iterator ci = table.getColumns().values().iterator(); ci .hasNext();) { final Column col = (Column) ci.next(); Log.debug("Writing column: " + col); // Give the column an ID. final String colMappedID = "" + this.currentElementID++; this.reverseMappedObjects.put(col, colMappedID); // Start column. this.openElement("column", xmlWriter); this.writeAttribute("id", colMappedID, xmlWriter); this.writeAttribute("name", col.getName(), xmlWriter); this.writeAttribute("visibleModified", Boolean.toString(col .isVisibleModified()), xmlWriter); this.writeListAttribute("inSchemaPartition", col .getSchemaPartitions().toArray(), xmlWriter); this.closeElement("column", xmlWriter); } // Write out keys inside each table. Remember relations as // we go along. for (final Iterator ki = table.getKeys().iterator(); ki.hasNext();) { final Key key = (Key) ki.next(); Log.debug("Writing key: " + key); // Give the key an ID. final String keyMappedID = "" + this.currentElementID++; this.reverseMappedObjects.put(key, keyMappedID); // What kind of key is it? String elem = null; if (key instanceof PrimaryKey) elem = "primaryKey"; else if (key instanceof ForeignKey) elem = "foreignKey"; else throw new DataModelException(Resources.get("unknownKey", key.getClass().getName())); // Write the key. this.openElement(elem, xmlWriter); this.writeAttribute("id", keyMappedID, xmlWriter); final List columnIds = new ArrayList(); for (int kci = 0; kci < key.getColumns().length; kci++) columnIds.add(this.reverseMappedObjects.get(key .getColumns()[kci])); this.writeListAttribute("columnIds", (String[]) columnIds .toArray(new String[0]), xmlWriter); this.writeAttribute("status", key.getStatus().toString(), xmlWriter); this.writeAttribute("visibleModified", Boolean.toString(key .isVisibleModified()), xmlWriter); this.closeElement(elem, xmlWriter); } // Finish table. this.closeElement("table", xmlWriter); } // Write relations. this.writeRelations(schema.getRelations(), false, xmlWriter); } /** * Internal method which does the work of writing out XML files and * generating those funky ID tags you see in them. * * @param mart * the mart to write. * @param xmlWriter * the Writer to write the XML to. * @param writeDTD * <tt>true</tt> if a DTD header line is to be included. * @throws IOException * if a write error occurs. * @throws DataModelException * if it encounters an object not writable under the current * DTD. * @throws PartitionException * if it encounters an object that does not reference a valid * partition. */ private void writeXML(final Mart mart, final Writer xmlWriter, final boolean writeDTD) throws IOException, DataModelException, PartitionException { // Write the headers. xmlWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); if (writeDTD) xmlWriter.write("<!DOCTYPE mart PUBLIC \"" + MartBuilderXML.DTD_PUBLIC_ID_START + MartBuilderXML.CURRENT_DTD_VERSION + MartBuilderXML.DTD_PUBLIC_ID_START + "\" \"" + MartBuilderXML.DTD_URL_START + MartBuilderXML.CURRENT_DTD_VERSION + MartBuilderXML.DTD_URL_END + "\">\n"); // Initialise the ID counter. this.reverseMappedObjects = new HashMap(); // Start by enclosing the whole lot in a <mart> tag. Log.debug("Writing mart: " + mart); this.openElement("mart", xmlWriter); this.writeAttribute("outputDatabase", mart.getOutputDatabase(), xmlWriter); this.writeAttribute("outputSchema", mart.getOutputSchema(), xmlWriter); this.writeAttribute("outputHost", mart.getOutputHost(), xmlWriter); this.writeAttribute("outputPort", mart.getOutputPort(), xmlWriter); this.writeAttribute("overrideHost", mart.getOverrideHost(), xmlWriter); this.writeAttribute("overridePort", mart.getOverridePort(), xmlWriter); this.writeAttribute("nameCase", "" + mart.getCase(), xmlWriter); this.writeAttribute("hideMaskedDataSets", Boolean.toString(mart .isHideMaskedDataSets()), xmlWriter); this.writeAttribute("hideMaskedSchemas", Boolean.toString(mart .isHideMaskedSchemas()), xmlWriter); // Write out each schema. final Set externalRelations = new HashSet(); for (final Iterator i = mart.getSchemas().values().iterator(); i .hasNext();) { final Schema schema = (Schema) i.next(); this.writeSchema(schema, xmlWriter); for (final Iterator j = schema.getRelations().iterator(); j .hasNext();) { final Relation rel = (Relation) j.next(); if (rel.isExternal()) externalRelations.add(rel); } } // Write out relations. this.writeRelations(externalRelations, true, xmlWriter); // Write out datasets. for (final Iterator dsi = mart.getDataSets().values().iterator(); dsi .hasNext();) { final DataSet ds = (DataSet) dsi.next(); // Get schema and dataset mods. Log.debug("Writing dataset: " + ds); this.openElement("dataset", xmlWriter); this.writeAttribute("name", ds.getName(), xmlWriter); this.writeAttribute("centralTableId", (String) this.reverseMappedObjects .get(ds.getCentralTable()), xmlWriter); this.writeAttribute("optimiser", ds.getDataSetOptimiserType() .getName(), xmlWriter); this.writeAttribute("invisible", Boolean.toString(ds.isInvisible()), xmlWriter); this.writeAttribute("masked", Boolean.toString(ds.isMasked()), xmlWriter); this.writeAttribute("hideMasked", Boolean.toString(ds .isHideMasked()), xmlWriter); this.writeAttribute("indexOptimiser", Boolean.toString(ds .isIndexOptimiser()), xmlWriter); // Write out visibleModified keys (toString()) for // all vismod relations, keys, and columns. Log.debug("Writing visible modified keys/rels/cols"); final Set vismodKeys = new HashSet(); for (final Iterator i = ds.getRelations().iterator(); i.hasNext();) { final Relation rel = (Relation) i.next(); if (rel.isVisibleModified()) vismodKeys.add(rel.toString()); } for (final Iterator i = ds.getTables().values().iterator(); i .hasNext();) { final Table tbl = (Table) i.next(); for (final Iterator j = tbl.getKeys().iterator(); j.hasNext();) { final Key k = (Key) j.next(); if (k.isVisibleModified()) vismodKeys.add(k.toString()); } for (final Iterator j = tbl.getColumns().values().iterator(); j .hasNext();) { final Column col = (Column) j.next(); if (col.isVisibleModified()) vismodKeys.add(col.toString()); } } for (final Iterator i = vismodKeys.iterator(); i.hasNext();) { final String key = (String) i.next(); this.openElement("visibleModified", xmlWriter); this.writeAttribute("key", key, xmlWriter); this.closeElement("visibleModified", xmlWriter); } Log.debug("Writing global modifications"); for (final Iterator s = mart.getSchemas().values().iterator(); s .hasNext();) { final Schema sch = (Schema) s.next(); // Write out relation special stuff. for (final Iterator i = sch.getRelations().iterator(); i .hasNext();) { final Relation r = (Relation) i.next(); // Write out subclass relations inside dataset. if (r.isSubclassRelation(ds)) { this.openElement("subclassRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("subclassRelation", xmlWriter); } // Write out merged tables inside dataset. if (r.isMergeRelation(ds)) { this.openElement("mergedRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("mergedRelation", xmlWriter); } // Masked relations. if (r.isMaskRelation(ds)) { this.openElement("maskedRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("maskedRelation", xmlWriter); } // Loopback relations. if (r.isLoopbackRelation(ds)) { this.openElement("loopbackRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.writeAttribute("diffColumnId", (String) this.reverseMappedObjects.get(r .getLoopbackRelation(ds)), xmlWriter); this.closeElement("loopbackRelation", xmlWriter); } // Force relations. if (r.isForceRelation(ds)) { this.openElement("forcedRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("forcedRelation", xmlWriter); } // Compound relations. if (r.isCompoundRelation(ds)) { final CompoundRelationDefinition def = r .getCompoundRelation(ds); this.openElement("compoundRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.writeAttribute("n", "" + def.getN(), xmlWriter); this.writeAttribute("parallel", "" + def.isParallel(), xmlWriter); this.closeElement("compoundRelation", xmlWriter); } // Unrolled relations. if (r.getUnrolledRelation(ds) != null) { final UnrolledRelationDefinition def = r .getUnrolledRelation(ds); this.openElement("unrolledRelation", xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.writeAttribute("columnId", (String) this.reverseMappedObjects.get(def .getNameColumn()), xmlWriter); this.writeAttribute("reversed", "" + def.isReversed(), xmlWriter); this.closeElement("unrolledRelation", xmlWriter); } } // Write out per-table mods. for (final Iterator i = sch.getTables().values().iterator(); i .hasNext();) { final Table tbl = (Table) i.next(); // Big table. if (tbl.getBigTable(ds) > 0) { this.openElement("bigTable", xmlWriter); this.writeAttribute("tableId", (String) this.reverseMappedObjects.get(tbl), xmlWriter); this.writeAttribute("bigness", "" + tbl.getBigTable(ds), xmlWriter); this.closeElement("bigTable", xmlWriter); } } } // Write out dstable mods from each table. for (final Iterator i = ds.getTables().values().iterator(); i .hasNext();) { final DataSetTable dsTable = (DataSetTable) i.next(); Log.debug("Writing modifications for " + dsTable); // Write out masked tables inside dataset. if (dsTable.isDimensionMasked()) { this.openElement("maskedTable", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("maskedTable", xmlWriter); } // Rename table. final String renameTbl = dsTable.getTableRename(); if (renameTbl != null) { this.openElement("renamedTable", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("newName", renameTbl, xmlWriter); this.closeElement("renamedTable", xmlWriter); } // Explain-hide-masked table. if (dsTable.isExplainHideMasked()) { this.openElement("explainHideMasked", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("explainHideMasked", xmlWriter); } // Explain-hide-masked table. if (dsTable.isTableHideMasked()) { this.openElement("tableHideMasked", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("tableHideMasked", xmlWriter); } // Distinct table. if (dsTable.isDistinctTable()) { this.openElement("distinctRows", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("distinctRows", xmlWriter); } // No-left-join table. if (dsTable.isNoFinalLeftJoin()) { this.openElement("noFinalLeftJoin", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("noFinalLeftJoin", xmlWriter); } // No-optimiser table. if (dsTable.isSkipOptimiser()) { this.openElement("skipOptimiser", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("skipOptimiser", xmlWriter); } // No-index-optimiser table. if (dsTable.isSkipIndexOptimiser()) { this.openElement("skipIndexOptimiser", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.closeElement("skipIndexOptimiser", xmlWriter); } // Write out dscol mods from each table col. for (final Iterator j = dsTable.getColumns().values() .iterator(); j.hasNext();) { final DataSetColumn dsCol = (DataSetColumn) j.next(); if (dsCol.isColumnMasked()) { this.openElement("maskedColumn", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("colKey", dsCol.getName(), xmlWriter); this.closeElement("maskedColumn", xmlWriter); } // Write out indexed columns. if (dsCol.isColumnIndexed()) { this.openElement("indexedColumn", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("colKey", dsCol.getName(), xmlWriter); this.closeElement("indexedColumn", xmlWriter); } // Write out split optimiser columns. if (dsCol.getSplitOptimiserColumn() != null) { final SplitOptimiserColumnDef def = dsCol .getSplitOptimiserColumn(); this.openElement("splitOptimiser", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("colKey", dsCol.getName(), xmlWriter); this.writeAttribute("contentCol", def.getContentCol(), xmlWriter); this.writeAttribute("separator", def.getSeparator(), xmlWriter); this.writeAttribute("prefix", "" + def.isPrefix(), xmlWriter); this.writeAttribute("suffix", "" + def.isSuffix(), xmlWriter); this.writeAttribute("size", "" + def.getSize(), xmlWriter); this.closeElement("splitOptimiser", xmlWriter); } // Expression column. if (dsCol instanceof ExpressionColumn) { final ExpressionColumnDefinition expcol = ((ExpressionColumn) dsCol) .getDefinition(); this.openElement("expressionColumn", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("colKey", dsCol.getName(), xmlWriter); final StringBuffer cols = new StringBuffer(); final StringBuffer names = new StringBuffer(); for (final Iterator z = expcol.getAliases().entrySet() .iterator(); z.hasNext();) { final Map.Entry entry3 = (Map.Entry) z.next(); cols.append((String) entry3.getKey()); names.append((String) entry3.getValue()); if (z.hasNext()) { cols.append(','); names.append(','); } } this.writeAttribute("aliasColumnNames", cols.toString(), xmlWriter); this.writeAttribute("aliasNames", names.toString(), xmlWriter); this.writeAttribute("expression", expcol .getExpression(), xmlWriter); this.writeAttribute("groupBy", "" + expcol.isGroupBy(), xmlWriter); this.closeElement("expressionColumn", xmlWriter); } // Rename column. final String renameCol = dsCol.getColumnRename(); if (renameCol != null) { this.openElement("renamedColumn", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("colKey", dsCol.getName(), xmlWriter); this.writeAttribute("newName", renameCol, xmlWriter); this.closeElement("renamedColumn", xmlWriter); } } for (final Iterator s = mart.getSchemas().values().iterator(); s .hasNext();) { final Schema sch = (Schema) s.next(); // Write out relation special stuff. for (final Iterator j = sch.getRelations().iterator(); j .hasNext();) { final Relation r = (Relation) j.next(); // Alternative joins. if (r.isAlternativeJoin(ds, dsTable.getName())) { this.openElement("alternativeJoin", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("alternativeJoin", xmlWriter); } // Mask relations. if (r.isMaskRelation(ds, dsTable.getName()) && !r.isMaskRelation(ds)) { this.openElement("maskedRelation", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("maskedRelation", xmlWriter); } // Loopback relations. if (r.isLoopbackRelation(ds, dsTable.getName())) { this.openElement("loopbackRelation", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.writeAttribute("diffColumnId", (String) this.reverseMappedObjects.get(r .getLoopbackRelation(ds, dsTable .getName())), xmlWriter); this.closeElement("loopbackRelation", xmlWriter); } // Force relations. if (r.isForceRelation(ds, dsTable.getName())) { this.openElement("forcedRelation", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this.closeElement("forcedRelation", xmlWriter); } // Compound relations. if (r.isCompoundRelation(ds, dsTable.getName())) { final CompoundRelationDefinition def = r .getCompoundRelation(ds, dsTable.getName()); this.openElement("compoundRelation", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects.get(r), xmlWriter); this .writeAttribute("n", "" + def.getN(), xmlWriter); this.writeAttribute("parallel", "" + def.isParallel(), xmlWriter); this.closeElement("compoundRelation", xmlWriter); } // Restrict relations. if (r.isRestrictRelation(ds, dsTable.getName())) { final CompoundRelationDefinition cdef = r .getCompoundRelation(ds, dsTable.getName()); final int maxIteration = cdef == null ? 1 : cdef .getN(); RestrictedRelationDefinition def = null; // Loop over known compound indices. for (int iteration = 0; iteration < maxIteration; iteration++) { def = r.getRestrictRelation(ds, dsTable .getName(), iteration); if (def == null) continue; this.openElement("restrictedRelation", xmlWriter); this.writeAttribute("tableKey", dsTable .getName(), xmlWriter); this.writeAttribute("relationId", (String) this.reverseMappedObjects .get(r), xmlWriter); this.writeAttribute("index", "" + iteration, xmlWriter); final StringBuffer lcols = new StringBuffer(); final StringBuffer lnames = new StringBuffer(); for (final Iterator a = def.getLeftAliases() .entrySet().iterator(); a.hasNext();) { final Map.Entry entry4 = (Map.Entry) a .next(); lcols .append((String) this.reverseMappedObjects .get((Column) entry4 .getKey())); lnames.append((String) entry4.getValue()); if (a.hasNext()) { lcols.append(','); lnames.append(','); } } this.writeAttribute("leftAliasColumnIds", lcols .toString(), xmlWriter); this.writeAttribute("leftAliasNames", lnames .toString(), xmlWriter); final StringBuffer rcols = new StringBuffer(); final StringBuffer rnames = new StringBuffer(); for (final Iterator a = def.getRightAliases() .entrySet().iterator(); a.hasNext();) { final Map.Entry entry4 = (Map.Entry) a .next(); rcols .append((String) this.reverseMappedObjects .get((Column) entry4 .getKey())); rnames.append((String) entry4.getValue()); if (a.hasNext()) { rcols.append(','); rnames.append(','); } } this.writeAttribute("rightAliasColumnIds", rcols.toString(), xmlWriter); this.writeAttribute("rightAliasNames", rnames .toString(), xmlWriter); this.writeAttribute("expression", def .getExpression(), xmlWriter); this.closeElement("restrictedRelation", xmlWriter); } } } // Write out per-table mods. for (final Iterator j = sch.getTables().values().iterator(); j .hasNext();) { final Table tbl = (Table) j.next(); // Transform starts. if (tbl.isTransformStart(ds, dsTable.getName())) { this.openElement("transformStart", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this .writeAttribute("tableId", (String) this.reverseMappedObjects .get(tbl), xmlWriter); this.closeElement("transformStart", xmlWriter); } // Big table. if (tbl.getBigTable(ds, dsTable.getName()) > 0) { this.openElement("bigTable", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this .writeAttribute("tableId", (String) this.reverseMappedObjects .get(tbl), xmlWriter); this.writeAttribute("bigness", "" + tbl.getBigTable(ds, dsTable.getName()), xmlWriter); this.closeElement("bigTable", xmlWriter); } // Restricted table. if (tbl.getRestrictTable(ds, dsTable.getName()) != null) { final RestrictedTableDefinition def = tbl .getRestrictTable(ds, dsTable.getName()); this.openElement("restrictedTable", xmlWriter); this.writeAttribute("tableKey", dsTable.getName(), xmlWriter); this .writeAttribute("tableId", (String) this.reverseMappedObjects .get(tbl), xmlWriter); final StringBuffer cols = new StringBuffer(); final StringBuffer names = new StringBuffer(); for (final Iterator z = def.getAliases().entrySet() .iterator(); z.hasNext();) { final Map.Entry entry3 = (Map.Entry) z.next(); cols.append((String) this.reverseMappedObjects .get((Column) entry3.getKey())); names.append((String) entry3.getValue()); if (z.hasNext()) { cols.append(','); names.append(','); } } this.writeAttribute("aliasColumnIds", cols .toString(), xmlWriter); this.writeAttribute("aliasNames", names.toString(), xmlWriter); this.writeAttribute("expression", def .getExpression(), xmlWriter); this.closeElement("restrictedTable", xmlWriter); } } } } // Finish dataset. this.closeElement("dataset", xmlWriter); } // Write out partition tables. for (final Iterator dsi = mart.getPartitionTables().iterator(); dsi .hasNext();) { final PartitionTable pt = (PartitionTable) dsi.next(); Log.debug("Writing dataset partition table: " + pt); this.openElement("datasetPartitionTable", xmlWriter); this.writeAttribute("name", pt.getName(), xmlWriter); this .writeListAttribute("selectedColumns", (String[]) pt .getSelectedColumnNames().toArray(new String[0]), xmlWriter); // Write out partition regex columns. Log.debug("Writing partition regexes"); for (final Iterator i = pt.getSelectedColumnNames().iterator(); i .hasNext();) { final String colName = (String) i.next(); if (colName.equals(PartitionTable.DIV_COLUMN)) continue; final PartitionColumn pcol = (PartitionColumn) pt.getColumns() .get(colName); if (pcol.getRegexMatch() != null && pcol.getRegexReplace() != null) { this.openElement("partitionRegex", xmlWriter); this.writeAttribute("name", pcol.getName(), xmlWriter); this.writeAttribute("match", pcol.getRegexMatch(), xmlWriter); this.writeAttribute("replace", pcol.getRegexReplace(), xmlWriter); this.closeElement("partitionRegex", xmlWriter); } } // Write out applications. Log.debug("Writing partition applications"); for (final Iterator j = pt.getAllApplications().entrySet() .iterator(); j.hasNext();) { final Map.Entry entry = (Map.Entry) j.next(); final DataSet target = (DataSet) entry.getKey(); for (final Iterator l = ((Map) entry.getValue()).entrySet() .iterator(); l.hasNext();) { final Map.Entry entry3 = (Map.Entry) l.next(); final PartitionTableApplication pta = (PartitionTableApplication) ((WeakReference) entry3 .getValue()).get(); if (pta == null) continue; this.openElement("partitionApplication", xmlWriter); this.writeAttribute("name", target.getName(), xmlWriter); this.writeAttribute("dimension", (String) entry3.getKey(), xmlWriter); final List pCols = new ArrayList(); final List dsCols = new ArrayList(); final List rels = new ArrayList(); final List nameCols = new ArrayList(); final List compounds = new ArrayList(); for (final Iterator k = pta.getPartitionAppliedRows() .iterator(); k.hasNext();) { final PartitionAppliedRow prow = (PartitionAppliedRow) k .next(); pCols.add(prow.getPartitionCol()); dsCols.add(prow.getRootDataSetCol()); rels.add((String) this.reverseMappedObjects.get(prow .getRelation())); nameCols.add(prow.getNamePartitionCol()); compounds.add(new Integer(prow.getCompound())); } this.writeListAttribute("pCols", (String[]) pCols .toArray(new String[0]), xmlWriter); this.writeListAttribute("dsCols", (String[]) dsCols .toArray(new String[0]), xmlWriter); this.writeListAttribute("relationIds", (String[]) rels .toArray(new String[0]), xmlWriter); this.writeListAttribute("nameCols", (String[]) nameCols .toArray(new String[0]), xmlWriter); this.writeListAttribute("compounds", (Integer[]) compounds .toArray(new Integer[0]), xmlWriter); this.closeElement("partitionApplication", xmlWriter); } } // Finish dataset partition table. this.closeElement("datasetPartitionTable", xmlWriter); } // Finished! Close the mart tag. this.closeElement("mart", xmlWriter); // Flush. xmlWriter.flush(); } public void endDocument() throws SAXException { // No action required. } public void endElement(final String namespaceURI, final String sName, final String qName) throws SAXException { // Work out what element it is we are closing. String eName = sName; if ("".equals(eName)) eName = qName; // Pop the element off the stack so that the next element // knows that it is inside the parent of this one. this.objectStack.pop(); } public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException { Log.debug("Resolving XML entity " + publicId + " " + systemId); // If the public ID is our own DTD version, then we can use our // own copy of the DTD in our resources bundle. MartBuilderXML.currentReadingDTDVersion = null; for (int i = 0; i < MartBuilderXML.SUPPORTED_DTD_VERSIONS.length && MartBuilderXML.currentReadingDTDVersion == null; i++) { final String currPub = MartBuilderXML.DTD_PUBLIC_ID_START + MartBuilderXML.SUPPORTED_DTD_VERSIONS[i] + MartBuilderXML.DTD_PUBLIC_ID_END; final String currUrl = MartBuilderXML.DTD_URL_START + MartBuilderXML.SUPPORTED_DTD_VERSIONS[i] + MartBuilderXML.DTD_URL_END; if (currPub.equals(publicId) || currUrl.equals(systemId)) MartBuilderXML.currentReadingDTDVersion = MartBuilderXML.SUPPORTED_DTD_VERSIONS[i]; } if (MartBuilderXML.currentReadingDTDVersion != null) { final String dtdDoc = "MartBuilder-" + MartBuilderXML.currentReadingDTDVersion + ".dtd"; Log.debug("Resolved to " + dtdDoc); return new InputSource(Resources.getResourceAsStream(dtdDoc)); } // By returning null we allow the default behaviour for all other // DTDs. else { Log.debug("Not resolved"); return null; } } public void startDocument() throws SAXException { // Reset all our maps of objects to IDs and clear // the stack of objects waiting to be processed. Log.debug("Started parsing XML document"); this.mappedObjects = new HashMap(); this.reverseMappedObjects = new HashMap(); this.objectStack = new Stack(); } public void startElement(final String namespaceURI, final String sName, final String qName, final Attributes attrs) throws SAXException { // Work out the name of the tag we are being asked to process. String eName = sName; if ("".equals(eName)) eName = qName; // Construct a set of attributes from the tag. final Map attributes = new HashMap(); if (attrs != null) for (int i = 0; i < attrs.getLength(); i++) { // Work out the name of the attribute. String aName = attrs.getLocalName(i); if ("".equals(aName)) aName = attrs.getQName(i); // Store the attribute and value. final String aValue = attrs.getValue(i); attributes.put(aName, aValue.replaceAll(""", "\"") .replaceAll("<", "<").replaceAll(">", ">") .replaceAll("&", "&")); } // Now, attempt to recognise the tag by checking its name // against a set of names known to us. Log.debug("Reading tag " + eName + " with attributes " + attributes); // Start by assuming the tag produces an unnested element; Object element = ""; // Mart (top-level only). if ("mart".equals(eName)) { // Start building a new mart. There can only be one mart tag // per file, as if more than one is found, the later tags // will override the earlier ones. final Mart mart = new Mart(); mart.setOutputDatabase((String) attributes.get("outputDatabase")); mart.setOutputSchema((String) attributes.get("outputSchema")); mart.setOutputHost((String) attributes.get("outputHost")); mart.setOutputPort((String) attributes.get("outputPort")); mart.setOverrideHost((String) attributes.get("overrideHost")); mart.setOverridePort((String) attributes.get("overridePort")); mart.setHideMaskedSchemas(Boolean.valueOf( (String) attributes.get("hideMaskedSchemas")) .booleanValue()); mart.setHideMaskedDataSets(Boolean.valueOf( (String) attributes.get("hideMaskedDataSets")) .booleanValue()); // Need check to be safe against pre-0.7 versions. if (attributes.containsKey("nameCase")) mart.setCase(Integer.parseInt((String) attributes .get("nameCase"))); element = this.constructedMart = mart; } // JDBC schema (anywhere, optionally inside schema group). else if ("jdbcSchema".equals(eName)) { // Start a new JDBC schema. final String uniqueId = (String) attributes.get("uniqueId"); // Does it have a password? (optional) String password = ""; if (attributes.containsKey("password")) password = (String) attributes.get("password"); // Load the compulsory attributes. final String driverClassName = (String) attributes .get("driverClassName"); final String url = (String) attributes.get("url"); final String databaseName = (String) attributes.get("databaseName"); final String schemaName = (String) attributes.get("schemaName"); final String username = (String) attributes.get("username"); final String name = (String) attributes.get("name"); final boolean keyguessing = Boolean.valueOf( (String) attributes.get("keyguessing")).booleanValue(); final boolean masked = Boolean.valueOf( (String) attributes.get("masked")).booleanValue(); final boolean hideMasked = Boolean.valueOf( (String) attributes.get("hideMasked")).booleanValue(); // Does it have partitions? final String partitionRegex = (String) attributes .get("partitionRegex"); final String partitionExpression = (String) attributes .get("partitionExpression"); // Construct the JDBC schema. try { final Schema schema = new JDBCSchema(this.constructedMart, driverClassName, url, databaseName, schemaName, username, password, name, keyguessing, partitionRegex, partitionExpression); schema.setMasked(masked); schema.setHideMasked(hideMasked); // Update the unique ID. if (uniqueId != null) schema.setUniqueId(Integer.parseInt(uniqueId)); // Return to normal. schema.storeInHistory(); // Add the schema directly to the mart if outside a group. this.constructedMart.getSchemas().put(schema.getOriginalName(), schema); element = schema; } catch (final Exception e) { throw new SAXException(e); } } // Table (inside table provider). else if ("table".equals(eName)) { // Start a new table. // What schema does it belong to? Throw a wobbly if not // currently inside a schema. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof Schema)) throw new SAXException(Resources.get("tableOutsideSchema")); final Schema schema = (Schema) this.objectStack.peek(); // Get the name and id as these are common features. final String id = (String) attributes.get("id"); final String uniqueId = (String) attributes.get("uniqueId"); final String name = (String) attributes.get("name"); final boolean ignore = Boolean.valueOf( (String) attributes.get("ignore")).booleanValue(); final String[] schemaPartitions = this.readListAttribute( (String) attributes.get("inSchemaPartition"), false); // What kind of schema? if (schema instanceof DataSet && MartBuilderXML.currentReadingDTDVersion.equals("0.5")) // In this case we don't care, because we need this // for backward compatibility with 0.5. So, ignore it // with no warning. We put a dummy DataSetTable on // the stack. element = new DataSetTable(name, (DataSet) schema, DataSetTableType.MAIN, null, null, 1); else if (schema instanceof Schema) try { final Table table = new Table(schema, name); table.setMasked(ignore); table.getSchemaPartitions().clear(); for (int i = 0; i < schemaPartitions.length; i++) table.getSchemaPartitions().add( schemaPartitions[i].intern()); schema.getTables().put(table.getName(), table); element = table; } catch (final Exception e) { throw new SAXException(e); } else throw new SAXException(Resources.get("unknownSchemaType", schema.getClass().getName())); // Update the unique ID. if (uniqueId != null) ((Table) element).setUniqueId(Integer.parseInt(uniqueId)); // Store it in the map of IDed objects. this.mappedObjects.put(id, element); } // Column (inside table). else if ("column".equals(eName)) { // What table does it belong to? Throw a wobbly if not inside one. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof Table)) throw new SAXException(Resources.get("columnOutsideTable")); final Table tbl = (Table) this.objectStack.peek(); // Get the id and name as these are common features. final String id = (String) attributes.get("id"); final String name = (String) attributes.get("name"); final boolean visibleModified = Boolean.valueOf( (String) attributes.get("visibleModified")).booleanValue(); final String[] schemaPartitions = this.readListAttribute( (String) attributes.get("inSchemaPartition"), false); try { // DataSet table column? if (tbl instanceof DataSetTable && MartBuilderXML.currentReadingDTDVersion .equals("0.5")) { // Since 0.5 we don't bother reading this stuff. // But, we don't thrown an exception as it is a valid // tag under 0.5. } // Generic column? else if (tbl instanceof Table) { final Column column = new Column(tbl, name); column.setVisibleModified(visibleModified); column.getSchemaPartitions().clear(); for (int i = 0; i < schemaPartitions.length; i++) column.getSchemaPartitions().add( schemaPartitions[i].intern()); tbl.getColumns().put(column.getName(), column); element = column; } // Others else throw new SAXException(Resources.get("unknownTableType", tbl.getClass().getName())); } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } // Store it in the map of IDed objects. this.mappedObjects.put(id, element); } // Primary key (inside table). else if ("primaryKey".equals(eName)) { // What table does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof Table)) throw new SAXException("pkOutsideTable"); final Table tbl = (Table) this.objectStack.peek(); // We don't do these for dataset tables since 0.6 as they // get regenerated automatically. if (tbl instanceof DataSetTable) return; // Get the ID. final String id = (String) attributes.get("id"); try { // Work out what status the key is. final ComponentStatus status = ComponentStatus .get((String) attributes.get("status")); final boolean visibleModified = Boolean.valueOf( (String) attributes.get("visibleModified")) .booleanValue(); // Decode the column IDs from the comma-separated list. final String[] pkColIds = this.readListAttribute( (String) attributes.get("columnIds"), false); final Column[] pkCols = new Column[pkColIds.length]; for (int i = 0; i < pkColIds.length; i++) pkCols[i] = (Column) this.mappedObjects.get(pkColIds[i]); // Make the key. final PrimaryKey pk = new PrimaryKey(pkCols); pk.setStatus(status); pk.setVisibleModified(visibleModified); // Assign it to the table. tbl.setPrimaryKey(pk); element = pk; } catch (final Exception e) { throw new SAXException(e); } // Store it in the map of IDed objects. this.mappedObjects.put(id, element); } // Foreign key (inside table). else if ("foreignKey".equals(eName)) { // What table does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof Table)) throw new SAXException(Resources.get("fkOutsideTable")); final Table tbl = (Table) this.objectStack.peek(); // We don't do these for dataset tables since 0.6 as they // get regenerated automatically. if (tbl instanceof DataSetTable) return; // Get the ID. final String id = (String) attributes.get("id"); try { // Work out what status it is. final ComponentStatus status = ComponentStatus .get((String) attributes.get("status")); final boolean visibleModified = Boolean.valueOf( (String) attributes.get("visibleModified")) .booleanValue(); // Decode the column IDs from the comma-separated list. final String[] fkColIds = this.readListAttribute( (String) attributes.get("columnIds"), false); final Column[] fkCols = new Column[fkColIds.length]; for (int i = 0; i < fkColIds.length; i++) fkCols[i] = (Column) this.mappedObjects.get(fkColIds[i]); // Make the key. final ForeignKey fk = new ForeignKey(fkCols); fk.setStatus(status); fk.setVisibleModified(visibleModified); // Add it to the table. tbl.getForeignKeys().add(fk); element = fk; } catch (final Exception e) { throw new SAXException(e); } // Store it in the map of IDed objects. this.mappedObjects.put(id, element); } // Relation (anywhere). else if ("relation".equals(eName)) { // Get the ID. final String id = (String) attributes.get("id"); try { // Work out status, cardinality, and look up the keys // at either end. final ComponentStatus status = ComponentStatus .get((String) attributes.get("status")); final Cardinality card = Cardinality.get((String) attributes .get("cardinality")); final Cardinality origCard = Cardinality .get((String) attributes.get("originalCardinality")); final Key firstKey = (Key) this.mappedObjects.get(attributes .get("firstKeyId")); final Key secondKey = (Key) this.mappedObjects.get(attributes .get("secondKeyId")); final boolean visibleModified = Boolean.valueOf( (String) attributes.get("visibleModified")) .booleanValue(); // We don't do these for dataset tables since 0.6 as they // get regenerated automatically. We can tell this is a // dataset table because at least one key ID will not // be found. if (firstKey == null || secondKey == null) // Element must be something. element = null; else { // Make it final Relation rel = new Relation(firstKey, secondKey, card); firstKey.getRelations().add(rel); secondKey.getRelations().add(rel); // Set its status. if (origCard != null) rel.setOriginalCardinality(origCard); rel.setStatus(status); rel.setVisibleModified(visibleModified); element = rel; } } catch (final Exception e) { throw new SAXException(e); } // Store it in the map of IDed objects. this.mappedObjects.put(id, element); } // Merged Table (inside dataset). else if ("mergedRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("mergedRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); // Merge it. if (rel != null) rel.setMergeRelation(w, true); } catch (final Exception e) { throw new SAXException(e); } } // Table-hide-masked (inside dataset). else if ("tableHideMasked".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("tableHideMaskedOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Hide-mask it. w.getMods(tableKey, "tableHideMasked").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Explain-hide-masked (inside dataset). else if ("explainHideMasked".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("explainHideMaskedOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Hide-mask it. w.getMods(tableKey, "explainHideMasked").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Visible modified (inside dataset). else if ("visibleModified".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("visibleModifiedOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String key = (String) attributes.get("key"); // Vis-mod it. w.getMods(key, "visibleModified").put(key.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // No-left-join Table (inside dataset). else if ("noFinalLeftJoin".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("noFinalLeftJoinOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Distinct it. w.getMods(tableKey, "noFinalLeftJoin").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // No-optimiser Table (inside dataset). else if ("skipOptimiser".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("skipOptimiserOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Distinct it. w.getMods(tableKey, "skipOptimiser").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // No-index-optimiser Table (inside dataset). else if ("skipIndexOptimiser".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("skipIndexOptimiserOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Distinct it. w.getMods(tableKey, "skipIndexOptimiser").put( tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Distinct Table (inside dataset). else if ("distinctRows".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("distinctRowsOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Distinct it. w.getMods(tableKey, "distinctTable").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Masked Table (inside dataset). else if ("maskedTable".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("maskedTableOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the table. final String tableKey = (String) attributes.get("tableKey"); // Mask it. w.getMods(tableKey, "dimensionMasked").put(tableKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Masked Relation (inside dataset). else if ("maskedRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("maskedRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final String tableKey = (String) attributes.get("tableKey"); // Mask it. if (rel != null) if (tableKey == null) rel.setMaskRelation(w, true); else rel.setMaskRelation(w, tableKey, true); } catch (final Exception e) { throw new SAXException(e); } } // Transform-start Relation (inside dataset). else if ("transformStart".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("transformStartOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Table tbl = (Table) this.mappedObjects.get(attributes .get("tableId")); final String tableKey = (String) attributes.get("tableKey"); // Mask it. if (tbl != null) tbl.setTransformStart(w, tableKey, true); } catch (final Exception e) { throw new SAXException(e); } } // Alternative-join Relation (inside dataset). else if ("alternativeJoin".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("alternativeJoinOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final String tableKey = (String) attributes.get("tableKey"); // Mask it. if (rel != null) rel.setAlternativeJoin(w, tableKey, true); } catch (final Exception e) { throw new SAXException(e); } } // Compound Relation (inside dataset). else if ("compoundRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("compoundRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final String tableKey = (String) attributes.get("tableKey"); final Integer n = Integer.valueOf((String) attributes.get("n")); final boolean parallel = Boolean.valueOf( (String) attributes.get("parallel")).booleanValue(); // Compound it. if (rel != null && n != null) { final CompoundRelationDefinition def = new CompoundRelationDefinition( n.intValue(), parallel); if (tableKey == null) rel.setCompoundRelation(w, def); else rel.setCompoundRelation(w, tableKey, def); } } catch (final Exception e) { throw new SAXException(e); } } // Directional Relation (inside dataset). else if ("directionalRelation".equals(eName)) { // Ignore - relict from 0.6. } // Unrolled Relation (inside dataset). else if ("unrolledRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("unrolledRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final Column col = (Column) this.mappedObjects.get(attributes .get("columnId")); final boolean reversed = Boolean.valueOf( (String) attributes.get("reversed")).booleanValue(); // Unroll it. if (rel != null && col != null) rel.setUnrolledRelation(w, new UnrolledRelationDefinition( col, reversed)); } catch (final Exception e) { throw new SAXException(e); } } // Forced Relation (inside dataset). else if ("forcedRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("forcedRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final String tableKey = (String) attributes.get("tableKey"); // Force it. if (rel != null) if (tableKey == null) rel.setForceRelation(w, true); else rel.setForceRelation(w, tableKey, true); } catch (final Exception e) { throw new SAXException(e); } } // Looped-back Relation (inside dataset). else if ("loopbackRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("loopbackRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final Column col = attributes.containsKey("diffColumnId") ? (Column) this.mappedObjects .get(attributes.get("diffColumnId")) : null; final String tableKey = (String) attributes.get("tableKey"); // Loopback it. if (rel != null) if (tableKey == null) rel.setLoopbackRelation(w, col); else rel.setLoopbackRelation(w, tableKey, col); } catch (final Exception e) { throw new SAXException(e); } } // Subclass Relation (inside dataset). else if ("subclassRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("subclassRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); // Subclass it. if (rel != null) rel.setSubclassRelation(w, true); } catch (final Exception e) { throw new SAXException(e); } } // Masked Column (inside dataset). else if ("maskedColumn".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("maskedColumnOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final String tableKey = (String) attributes.get("tableKey"); final String colKey = (String) attributes.get("colKey"); // Mask it. w.getMods(tableKey, "columnMasked").put(colKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Indexdd Column (inside dataset). else if ("indexedColumn".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("indexedColumnOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final String tableKey = (String) attributes.get("tableKey"); final String colKey = (String) attributes.get("colKey"); // Index it. w.getMods(tableKey, "columnIndexed").put(colKey.intern(), null); } catch (final Exception e) { throw new SAXException(e); } } // Split Optimiser Column (inside dataset). else if ("splitOptimiser".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("splitOptimiserOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the relation. final String tableKey = (String) attributes.get("tableKey"); final String colKey = (String) attributes.get("colKey"); final String contentCol = (String) attributes.get("contentCol"); final String separator = (String) attributes.get("separator"); final boolean prefix = Boolean.valueOf( (String) attributes.get("prefix")).booleanValue(); final boolean suffix = Boolean.valueOf( (String) attributes.get("suffix")).booleanValue(); int size = 255; try { size = Integer.valueOf((String) attributes.get("size")) .intValue(); } catch (final NumberFormatException ne) { size = 255; } if (size < 1) size = 255; // Index it. final SplitOptimiserColumnDef def = new SplitOptimiserColumnDef( contentCol, separator); def.setPrefix(prefix); def.setSuffix(suffix); def.setSize(size); w.getMods(tableKey, "splitOptimiserColumn").put( colKey.intern(), def); } catch (final Exception e) { throw new SAXException(e); } } // Renamed table (inside dataset). else if ("renamedTable".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("renamedTableOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the values. final String tableKey = (String) attributes.get("tableKey"); final String newName = (String) attributes.get("newName"); w.getMods(tableKey, "tableRename").put(tableKey.intern(), newName); } catch (final Exception e) { throw new SAXException(e); } } // Renamed column (inside dataset). else if ("renamedColumn".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("renamedColumnOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the values. final String tableKey = (String) attributes.get("tableKey"); final String colKey = (String) attributes.get("colKey"); final String newName = (String) attributes.get("newName"); w.getMods(tableKey, "columnRename").put(colKey.intern(), newName); } catch (final Exception e) { throw new SAXException(e); } } // Restricted Table (inside dataset). else if ("restrictedTable".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("restrictedTableOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the restriction. final Table tbl = (Table) this.mappedObjects.get(attributes .get("tableId")); // Default to dataset-wide restriction if 0.5 syntax used. final String tableKey = (String) attributes.get("tableKey"); // Get the aliases to use for the first table. final Map aliases = new HashMap(); final String[] aliasColumnIds = this.readListAttribute( (String) attributes.get("aliasColumnIds"), false); // Remove final String[] aliasNames = this.readListAttribute( (String) attributes.get("aliasNames"), false); for (int i = 0; i < aliasColumnIds.length; i++) { final Column wcol = (Column) this.mappedObjects .get(aliasColumnIds[i]); if (wcol != null) aliases.put(wcol, aliasNames[i]); } // Get the expression to use. final String expr = (String) attributes.get("expression"); // Set up the restriction. if (expr != null && !aliases.isEmpty() && tableKey != null && tbl != null) { final RestrictedTableDefinition def = new RestrictedTableDefinition( expr, aliases); tbl.setRestrictTable(w, tableKey, def); } } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } } // Big Table (inside dataset). else if ("bigTable".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources.get("bigTableOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the restriction. final Table tbl = (Table) this.mappedObjects.get(attributes .get("tableId")); // Default to dataset-wide restriction if 0.5 syntax used. final String tableKey = (String) attributes.get("tableKey"); // Get the aliases to use for the first table. final int bigness = Integer.parseInt((String) attributes .get("bigness")); if (tableKey == null) tbl.setBigTable(w, bigness); else tbl.setBigTable(w, tableKey, bigness); } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } } // Expression Column (inside dataset). else if ("expressionColumn".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("expressionColumnOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { final String colKey = (String) attributes.get("colKey"); final String tableKey = (String) attributes.get("tableKey"); // Get the aliases to use for the first table. final Map aliases = new HashMap(); final String[] aliasColumnNames = this.readListAttribute( (String) attributes.get("aliasColumnNames"), false); final String[] aliasNames = this.readListAttribute( (String) attributes.get("aliasNames"), false); for (int i = 0; i < aliasColumnNames.length; i++) aliases.put(aliasColumnNames[i], aliasNames[i]); // Get the expression to use. final String expr = (String) attributes.get("expression"); final boolean groupBy = Boolean.valueOf( (String) attributes.get("groupBy")).booleanValue(); // Set the expression up. if (expr != null && !aliases.isEmpty() && tableKey != null && colKey != null) { final ExpressionColumnDefinition expdef = new ExpressionColumnDefinition( expr, aliases, groupBy, colKey); w.getMods(tableKey, "initialExpressions").put( colKey.intern(), expdef); } } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } } // Partitioned column (inside dataset). else if ("partitionedColumn".equals(eName)) { // Ignore - legacy from 0.6. } // Restricted Relation (inside dataset). else if ("restrictedRelation".equals(eName)) { // What dataset does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof DataSet)) throw new SAXException(Resources .get("restrictedRelationOutsideDataSet")); final DataSet w = (DataSet) this.objectStack.peek(); try { // Look up the restriction. final Relation rel = (Relation) this.mappedObjects .get(attributes.get("relationId")); final String tableKey = (String) attributes.get("tableKey"); final int index = Integer.parseInt((String) attributes .get("index")); // Get the aliases to use for the first table. final Map laliases = new HashMap(); final String[] laliasColumnIds = this.readListAttribute( (String) attributes.get("leftAliasColumnIds"), false); final String[] laliasNames = this.readListAttribute( (String) attributes.get("leftAliasNames"), false); for (int i = 0; i < laliasColumnIds.length; i++) { final Column wcol = (Column) this.mappedObjects .get(laliasColumnIds[i]); if (wcol != null) laliases.put(wcol, laliasNames[i]); } // and the second final Map raliases = new HashMap(); final String[] raliasColumnIds = this.readListAttribute( (String) attributes.get("rightAliasColumnIds"), false); final String[] raliasNames = this.readListAttribute( (String) attributes.get("rightAliasNames"), false); for (int i = 0; i < raliasColumnIds.length; i++) { final Column wcol = (Column) this.mappedObjects .get(raliasColumnIds[i]); if (wcol != null) raliases.put(wcol, raliasNames[i]); } // Get the expression to use. final String expr = (String) attributes.get("expression"); if (expr != null && rel != null && tableKey != null && !laliases.isEmpty() && !raliases.isEmpty()) { final RestrictedRelationDefinition def = new RestrictedRelationDefinition( expr, laliases, raliases); rel.setRestrictRelation(w, tableKey, def, index); } } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } } // DataSet (anywhere). else if ("dataset".equals(eName)) try { // Look up the name etc. // Resolve them all. final String name = (String) attributes.get("name"); final boolean invisible = Boolean.valueOf( (String) attributes.get("invisible")).booleanValue(); final boolean masked = Boolean.valueOf( (String) attributes.get("masked")).booleanValue(); final boolean hideMasked = Boolean.valueOf( (String) attributes.get("hideMasked")).booleanValue(); final Table centralTable = (Table) this.mappedObjects .get(attributes.get("centralTableId")); final String optType = (String) attributes.get("optimiser"); final boolean index = Boolean.valueOf( (String) attributes.get("indexOptimiser")) .booleanValue(); // Construct the dataset. final DataSet ds = new DataSet(this.constructedMart, centralTable, name); this.constructedMart.getDataSets() .put(ds.getOriginalName(), ds); // Work out the optimiser. DataSetOptimiserType opt = DataSetOptimiserType.NONE; try { opt = (DataSetOptimiserType) DataSetOptimiserType.class .getField(optType).get(null); } catch (final NoSuchFieldException nfe) { opt = DataSetOptimiserType.NONE; } // Assign the settings. ds.setDataSetOptimiserType(opt); ds.setInvisible(invisible); ds.setMasked(masked); ds.setHideMasked(hideMasked); ds.setIndexOptimiser(index); element = ds; } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } // DataSet partition table (anywhere). else if ("datasetPartitionTable".equals(eName)) try { // Convert a dataset into a partition table. final DataSet ds = (DataSet) this.constructedMart.getDataSets() .get((String) attributes.get("name")); final String[] selectedColumns = this.readListAttribute( (String) attributes.get("selectedColumns"), false); ds.setPartitionTable(true); ds.asPartitionTable().setSelectedColumnNames( Arrays.asList(selectedColumns)); element = ds.asPartitionTable(); } catch (final Exception e) { if (e instanceof SAXException) throw (SAXException) e; else throw new SAXException(e); } // Partition regex (inside partition table) else if ("partitionRegex".equals(eName)) { // What pt does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof PartitionTable)) throw new SAXException(Resources .get("partitionRegexOutsidePartitionTable")); final PartitionTable pt = (PartitionTable) this.objectStack.peek(); final String name = (String) attributes.get("name"); final String match = (String) attributes.get("match"); final String replace = (String) attributes.get("replace"); try { ((PartitionColumn) pt.getColumns().get(name)) .setRegexMatch(match); ((PartitionColumn) pt.getColumns().get(name)) .setRegexReplace(replace); } catch (final Exception e) { throw new SAXException(e); } } // Partition application (inside partition table) else if ("partitionApplication".equals(eName)) { // What pt does it belong to? Throw a wobbly if none. if (this.objectStack.empty() || !(this.objectStack.peek() instanceof PartitionTable)) throw new SAXException(Resources .get("partitionApplicationOutsidePartitionTable")); final PartitionTable pt = (PartitionTable) this.objectStack.peek(); final DataSet ds = (DataSet) this.constructedMart.getDataSets() .get((String) attributes.get("name")); String dimension = (String) attributes.get("dimension"); final String[] pCols = this.readListAttribute((String) attributes .get("pCols"), false); final String[] dsCols = this.readListAttribute((String) attributes .get("dsCols"), false); final String[] relIds = this.readListAttribute((String) attributes .get("relationIds"), true); final Relation[] rels = new Relation[relIds.length]; for (int i = 0; i < relIds.length; i++) rels[i] = (Relation) this.mappedObjects.get(relIds[i]); final String[] nameCols = this.readListAttribute( (String) attributes.get("nameCols"), false); final String[] compounds = this.readListAttribute( (String) attributes.get("compounds"), false); final PartitionTableApplication pta = new PartitionTableApplication( pt); for (int i = 0; i < pCols.length; i++) { final PartitionAppliedRow row = new PartitionAppliedRow( pCols[i], dsCols[i], nameCols[i], rels[i]); if (compounds.length > i) row.setCompound(Integer.parseInt(compounds[i])); pta.getPartitionAppliedRows().add(row); } if (dimension == null) dimension = PartitionTable.NO_DIMENSION; if (!dimension.equals(PartitionTable.NO_DIMENSION)) ds.getMods(dimension, "initialPTAs").put(dimension.intern(), pta); else pt.applyTo(ds, dimension, pta); } else throw new SAXException(Resources.get("unknownTag", eName)); // Stick the element on the stack so that the next element // knows what it is inside. this.objectStack.push(element); } }