/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.envers.configuration.internal.metadata; import java.util.Iterator; import javax.persistence.JoinColumn; import org.hibernate.envers.internal.tools.StringTools; import org.hibernate.mapping.Column; import org.hibernate.mapping.Formula; import org.hibernate.mapping.Selectable; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; /** * @author Adam Warski (adam at warski dot org) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) * @author Michal Skowronek (mskowr at o2 dot pl) */ public final class MetadataTools { private MetadataTools() { } public static Element addNativelyGeneratedId( Element parent, String name, String type, boolean useRevisionEntityWithNativeId) { final Element idMapping = parent.addElement( "id" ); idMapping.addAttribute( "name", name ).addAttribute( "type", type ); final Element generatorMapping = idMapping.addElement( "generator" ); if ( useRevisionEntityWithNativeId ) { generatorMapping.addAttribute( "class", "native" ); } else { generatorMapping.addAttribute( "class", "org.hibernate.envers.enhanced.OrderedSequenceGenerator" ); generatorMapping.addElement( "param" ).addAttribute( "name", "sequence_name" ).setText( "REVISION_GENERATOR" ); generatorMapping.addElement( "param" ) .addAttribute( "name", "table_name" ) .setText( "REVISION_GENERATOR" ); generatorMapping.addElement( "param" ).addAttribute( "name", "initial_value" ).setText( "1" ); generatorMapping.addElement( "param" ).addAttribute( "name", "increment_size" ).setText( "1" ); } // generatorMapping.addAttribute("class", "sequence"); // generatorMapping.addElement("param").addAttribute("name", "sequence").setText("custom"); return idMapping; } public static Element addProperty( Element parent, String name, String type, boolean insertable, boolean updateable, boolean key) { final Element propMapping; if ( key ) { propMapping = parent.addElement( "key-property" ); } else { propMapping = parent.addElement( "property" ); propMapping.addAttribute( "insert", Boolean.toString( insertable ) ); propMapping.addAttribute( "update", Boolean.toString( updateable ) ); } propMapping.addAttribute( "name", name ); if ( type != null ) { propMapping.addAttribute( "type", type ); } return propMapping; } public static Element addProperty(Element parent, String name, String type, boolean insertable, boolean key) { return addProperty( parent, name, type, insertable, false, key ); } public static Element addModifiedFlagProperty(Element parent, String propertyName, String suffix, String modifiedFlagName) { return addProperty( parent, (modifiedFlagName != null) ? modifiedFlagName : getModifiedFlagPropertyName( propertyName, suffix ), "boolean", true, false, false ); } public static String getModifiedFlagPropertyName(String propertyName, String suffix) { return propertyName + suffix; } private static void addOrModifyAttribute(Element parent, String name, String value) { final Attribute attribute = parent.attribute( name ); if ( attribute == null ) { parent.addAttribute( name, value ); } else { attribute.setValue( value ); } } /** * Column name shall be wrapped with '`' signs if quotation required. */ public static Element addOrModifyColumn(Element parent, String name) { final Element columnMapping = parent.element( "column" ); if ( columnMapping == null ) { return addColumn( parent, name, null, null, null, null, null, null ); } if ( !StringTools.isEmpty( name ) ) { addOrModifyAttribute( columnMapping, "name", name ); } return columnMapping; } /** * Adds new <code>column</code> element. Method assumes that the value of <code>name</code> attribute is already * wrapped with '`' signs if quotation required. It shall be invoked when column name is taken directly from configuration * file and not from {@link org.hibernate.mapping.PersistentClass} descriptor. */ public static Element addColumn( Element parent, String name, Integer length, Integer scale, Integer precision, String sqlType, String customRead, String customWrite) { return addColumn( parent, name, length, scale, precision, sqlType, customRead, customWrite, false ); } public static Element addColumn( Element parent, String name, Integer length, Integer scale, Integer precision, String sqlType, String customRead, String customWrite, boolean quoted) { final Element columnMapping = parent.addElement( "column" ); columnMapping.addAttribute( "name", quoted ? "`" + name + "`" : name ); if ( length != null ) { columnMapping.addAttribute( "length", length.toString() ); } if ( scale != null ) { columnMapping.addAttribute( "scale", Integer.toString( scale ) ); } if ( precision != null ) { columnMapping.addAttribute( "precision", Integer.toString( precision ) ); } if ( !StringTools.isEmpty( sqlType ) ) { columnMapping.addAttribute( "sql-type", sqlType ); } if ( !StringTools.isEmpty( customRead ) ) { columnMapping.addAttribute( "read", customRead ); } if ( !StringTools.isEmpty( customWrite ) ) { columnMapping.addAttribute( "write", customWrite ); } return columnMapping; } private static Element createEntityCommon( Document document, String type, AuditTableData auditTableData, String discriminatorValue, Boolean isAbstract) { final Element hibernateMapping = document.addElement( "hibernate-mapping" ); hibernateMapping.addAttribute( "auto-import", "false" ); final Element classMapping = hibernateMapping.addElement( type ); if ( auditTableData.getAuditEntityName() != null ) { classMapping.addAttribute( "entity-name", auditTableData.getAuditEntityName() ); } if ( discriminatorValue != null ) { classMapping.addAttribute( "discriminator-value", discriminatorValue ); } if ( !StringTools.isEmpty( auditTableData.getAuditTableName() ) ) { classMapping.addAttribute( "table", auditTableData.getAuditTableName() ); } if ( !StringTools.isEmpty( auditTableData.getSchema() ) ) { classMapping.addAttribute( "schema", auditTableData.getSchema() ); } if ( !StringTools.isEmpty( auditTableData.getCatalog() ) ) { classMapping.addAttribute( "catalog", auditTableData.getCatalog() ); } if ( isAbstract != null ) { classMapping.addAttribute( "abstract", isAbstract.toString() ); } return classMapping; } public static Element createEntity( Document document, AuditTableData auditTableData, String discriminatorValue, Boolean isAbstract) { return createEntityCommon( document, "class", auditTableData, discriminatorValue, isAbstract ); } public static Element createSubclassEntity( Document document, String subclassType, AuditTableData auditTableData, String extendsEntityName, String discriminatorValue, Boolean isAbstract) { final Element classMapping = createEntityCommon( document, subclassType, auditTableData, discriminatorValue, isAbstract ); classMapping.addAttribute( "extends", extendsEntityName ); return classMapping; } public static Element createJoin( Element parent, String tableName, String schema, String catalog) { final Element joinMapping = parent.addElement( "join" ); joinMapping.addAttribute( "table", tableName ); if ( !StringTools.isEmpty( schema ) ) { joinMapping.addAttribute( "schema", schema ); } if ( !StringTools.isEmpty( catalog ) ) { joinMapping.addAttribute( "catalog", catalog ); } return joinMapping; } public static void addColumns(Element anyMapping, Iterator selectables) { while ( selectables.hasNext() ) { final Selectable selectable = (Selectable) selectables.next(); if ( selectable.isFormula() ) { throw new FormulaNotSupportedException(); } addColumn( anyMapping, (Column) selectable ); } } /** * Adds <code>column</code> element with the following attributes (unless empty): <code>name</code>, * <code>length</code>, <code>scale</code>, <code>precision</code>, <code>sql-type</code>, <code>read</code> * and <code>write</code>. * * @param anyMapping Parent element. * @param column Column descriptor. */ public static void addColumn(Element anyMapping, Column column) { addColumn( anyMapping, column.getName(), column.getLength(), column.getScale(), column.getPrecision(), column.getSqlType(), column.getCustomRead(), column.getCustomWrite(), column.isQuoted() ); } @SuppressWarnings({"unchecked"}) private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) { final Iterator<Element> properties = element.elementIterator(); while ( properties.hasNext() ) { final Element property = properties.next(); if ( "column".equals( property.getName() ) ) { final Attribute nameAttr = property.attribute( "name" ); if ( nameAttr != null ) { nameAttr.setText( columnNameIterator.next() ); } } } } @SuppressWarnings({"unchecked"}) public static void prefixNamesInPropertyElement( Element element, String prefix, ColumnNameIterator columnNameIterator, boolean changeToKey, boolean insertable) { final Iterator<Element> properties = element.elementIterator(); while ( properties.hasNext() ) { final Element property = properties.next(); if ( "property".equals( property.getName() ) || "many-to-one".equals( property.getName() ) ) { final Attribute nameAttr = property.attribute( "name" ); if ( nameAttr != null ) { nameAttr.setText( prefix + nameAttr.getText() ); } changeNamesInColumnElement( property, columnNameIterator ); if ( changeToKey ) { property.setName( "key-" + property.getName() ); } if ( "property".equals( property.getName() ) ) { final Attribute insert = property.attribute( "insert" ); insert.setText( Boolean.toString( insertable ) ); } } } } /** * Adds <code>formula</code> element. * * @param element Parent element. * @param formula Formula descriptor. */ public static void addFormula(Element element, Formula formula) { element.addElement( "formula" ).setText( formula.getText() ); } /** * Adds all <code>column</code> or <code>formula</code> elements. * * @param element Parent element. * @param columnIterator Iterator pointing at {@link org.hibernate.mapping.Column} and/or * {@link org.hibernate.mapping.Formula} objects. */ public static void addColumnsOrFormulas(Element element, Iterator columnIterator) { while ( columnIterator.hasNext() ) { final Object o = columnIterator.next(); if ( o instanceof Column ) { addColumn( element, (Column) o ); } else if ( o instanceof Formula ) { addFormula( element, (Formula) o ); } } } /** * An iterator over column names. */ public abstract static class ColumnNameIterator implements Iterator<String> { } public static ColumnNameIterator getColumnNameIterator(final Iterator<Selectable> selectableIterator) { return new ColumnNameIterator() { public boolean hasNext() { return selectableIterator.hasNext(); } public String next() { final Selectable next = selectableIterator.next(); if ( next.isFormula() ) { throw new FormulaNotSupportedException(); } return ( (Column) next ).getName(); } public void remove() { selectableIterator.remove(); } }; } public static ColumnNameIterator getColumnNameIterator(final JoinColumn[] joinColumns) { return new ColumnNameIterator() { int counter; public boolean hasNext() { return counter < joinColumns.length; } public String next() { return joinColumns[counter++].name(); } public void remove() { throw new UnsupportedOperationException(); } }; } }