All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metawidget.inspector.hibernate.HibernateInspector Maven / Gradle / Ivy

The newest version!
// Metawidget
//
// This file is dual licensed under both the LGPL
// (http://www.gnu.org/licenses/lgpl-2.1.html) and the EPL
// (http://www.eclipse.org/org/documents/epl-v10.php). As a
// recipient of Metawidget, you may choose to receive it under either
// the LGPL or the EPL.
//
// Commercial licenses are also available. See http://metawidget.org
// for details.

package org.metawidget.inspector.hibernate;

import static org.metawidget.inspector.InspectionResultConstants.*;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.metawidget.config.iface.ResourceResolver;
import org.metawidget.inspector.iface.InspectorException;
import org.metawidget.inspector.impl.BaseXmlInspector;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.XmlUtils;
import org.metawidget.util.simple.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Inspector to look for relevant settings in hibernate.cfg.xml and mapping.hbm.xml files.
 *
 * @author Richard Kennard
 */

public class HibernateInspector
	extends BaseXmlInspector {

	//
	// Private statics
	//

	private static final String			HIBERNATE_CONFIGURATION_ELEMENT	= "hibernate-configuration";

	private static final String			HIBERNATE_MAPPING_ELEMENT		= "hibernate-mapping";

	private static final InputStream[]	EMPTY_INPUTSTREAM_ARRAY			= new InputStream[0];

	private static final String			PROPERTY_REF_ATTRIBUTE			= "property-ref";

	//
	// Private members
	//

	private final boolean				mHideIds;

	//
	// Constructor
	//

	public HibernateInspector( HibernateInspectorConfig config ) {

		super( config );

		mHideIds = config.isHideIds();
	}

	//
	// Protected methods
	//

	/**
	 * Overridden to search by name=, not type=.
	 */

	@Override
	protected String getTopLevelTypeAttribute() {

		return NAME;
	}

	@Override
	protected String getTypeAttribute() {

		return "class";
	}

	/**
	 * Hibernate supports extends via its <subclass> element.
	 */

	@Override
	protected String getExtendsAttribute() {

		return "extends";
	}

	/**
	 * Overridden to automatically drill into Hibernate Configuration files.
	 */

	@Override
	protected Element getDocumentElement( ResourceResolver resolver, InputStream... files )
		throws Exception {

		Document documentMaster = null;

		for ( InputStream file : files ) {
			Document documentParsed = XmlUtils.parse( file );

			if ( !documentParsed.hasChildNodes() ) {
				continue;
			}

			// If the document is a hibernate-configuration file...

			Element parsed = documentParsed.getDocumentElement();
			String nodeName = parsed.getNodeName();

			if ( HIBERNATE_CONFIGURATION_ELEMENT.equals( nodeName ) ) {
				// ...look up each hibernate-mapping file...

				Element mapping = XmlUtils.getChildNamed( documentParsed.getDocumentElement(), "session-factory", "mapping" );

				List inputStreamList = CollectionUtils.newArrayList();

				while ( mapping != null ) {
					inputStreamList.add( resolver.openResource( mapping.getAttribute( "resource" ) ) );
					mapping = XmlUtils.getSiblingNamed( mapping, "mapping" );
				}

				// ...and combine them

				parsed = getDocumentElement( resolver, inputStreamList.toArray( EMPTY_INPUTSTREAM_ARRAY ) );

				if ( documentMaster == null || !documentMaster.hasChildNodes() ) {
					documentMaster = parsed.getOwnerDocument();
					continue;
				}
			} else if ( HIBERNATE_MAPPING_ELEMENT.equals( nodeName ) ) {
				
				// ...otherwise, read hibernate-mapping files

				preprocessDocument( documentParsed );

				if ( documentMaster == null || !documentMaster.hasChildNodes() ) {
					documentMaster = documentParsed;
					continue;
				}
			} else {
				throw InspectorException.newException( "Expected an XML document starting with '" + HIBERNATE_CONFIGURATION_ELEMENT + "' or '" + HIBERNATE_MAPPING_ELEMENT + "', but got '" + nodeName + "'" );
			}

			XmlUtils.combineElements( documentMaster.getDocumentElement(), parsed, getTopLevelTypeAttribute(), getNameAttribute() );
		}

		if ( documentMaster == null ) {
			return null;
		}

		return documentMaster.getDocumentElement();
	}

	/**
	 * Prepend 'package' attribute to class 'name' and 'extends' attributes, and to 'class'
	 * attributes of children.
	 */

	@Override
	protected void preprocessDocument( Document document ) {

		Element root = document.getDocumentElement();
		String packagePrefix = root.getAttribute( "package" );

		if ( packagePrefix != null && !"".equals( packagePrefix ) ) {
			packagePrefix += StringUtils.SEPARATOR_DOT_CHAR;

			String topLevelAttribute = getTopLevelTypeAttribute();
			String extendsAttribute = getExtendsAttribute();
			Element child = XmlUtils.getFirstChildElement( root );

			while ( child != null ) {

				// 'name' attribute of 'class'/'subclass' element

				String name = child.getAttribute( topLevelAttribute );

				if ( name != null && !"".equals( name ) && name.indexOf( StringUtils.SEPARATOR_DOT_CHAR ) == -1 ) {
					child.setAttribute( topLevelAttribute, packagePrefix + name );
				}

				// 'extends' attribute of 'subclass' element

				String extendsClass = child.getAttribute( extendsAttribute );

				if ( extendsClass != null && !"".equals( extendsClass ) && extendsClass.indexOf( StringUtils.SEPARATOR_DOT_CHAR ) == -1 ) {
					child.setAttribute( extendsAttribute, packagePrefix + extendsClass );
				}

				// 'class' attributes of children

				prependPackageToClassAttribute( child, packagePrefix );
				child = XmlUtils.getNextSiblingElement( child );
			}
		}
	}

	@Override
	protected Map inspectProperty( Element toInspect ) {

		Map attributes = CollectionUtils.newHashMap();

		// Hibernate has a rich DTD for its mapping files. We try to parse it
		// without explicitly enumerating every possible type of node

		// Name

		String name = toInspect.getAttribute( NAME );

		// Some nodes (like meta) have no name

		if ( "".equals( name )) {
			return null;
		}

		attributes.put( NAME, name );

		// Do not just copy 'type': Hibernate types are not the POJO type

		String type = toInspect.getAttribute( TYPE );

		// Large

		if ( "clob".equals( type ) ) {
			attributes.put( TYPE, String.class.getName() );
			attributes.put( LARGE, TRUE );
		}

		// Class, on the other hand, IS a reliable indicator of POJO type

		String typeAttribute = getTypeAttribute();

		if ( toInspect.hasAttribute( typeAttribute ) ) {
			attributes.put( TYPE, toInspect.getAttribute( typeAttribute ) );
		}

		// Property/column elements
		// https://sourceforge.net/p/metawidget/discussion/747623/thread/f8ce6edd/?limit=25#0830

		inspectColumnAttributes( toInspect, attributes );
		Element columnElement = XmlUtils.getChildNamed( toInspect, "column" );

		if ( columnElement != null ) {
			inspectColumnAttributes( columnElement, attributes );
		}

		// Hidden

		String nodeName = toInspect.getNodeName();

		if ( mHideIds && "id".equals( nodeName ) ) {
			attributes.put( HIDDEN, TRUE );
		}

		if ( "bag".equals( nodeName ) || "list".equals( nodeName ) || "set".equals( nodeName ) ) {

			// Parameterized

			Element withClass = XmlUtils.getChildWithAttribute( toInspect, typeAttribute );

			if ( withClass != null ) {
				attributes.put( PARAMETERIZED_TYPE, withClass.getAttribute( typeAttribute ) );

				// Property Ref

				if ( withClass.hasAttribute( PROPERTY_REF_ATTRIBUTE ) ) {
					attributes.put( INVERSE_RELATIONSHIP, withClass.getAttribute( PROPERTY_REF_ATTRIBUTE ) );
				}
			}
		}

		// Property Ref

		if ( toInspect.hasAttribute( PROPERTY_REF_ATTRIBUTE ) ) {
			attributes.put( INVERSE_RELATIONSHIP, toInspect.getAttribute( PROPERTY_REF_ATTRIBUTE ) );
		}

		return attributes;
	}

	//
	// Private methods
	//

	/**
	 * Inspect attributes that can appear either on 'property' elements or 'column' elements.
	 */

	private void inspectColumnAttributes( Element toInspect, Map attributes ) {

		// Required

		if ( TRUE.equals( toInspect.getAttribute( "not-null" ) ) ) {
			attributes.put( REQUIRED, TRUE );
		}

		// Length

		if ( toInspect.hasAttribute( "length" ) ) {
			attributes.put( MAXIMUM_LENGTH, toInspect.getAttribute( "length" ) );
		}
	}

	private void prependPackageToClassAttribute( Element element, String packagePrefix ) {

		// For each child...

		NodeList children = element.getChildNodes();

		String typeAttribute = getTypeAttribute();

		for ( int loop = 0, length = children.getLength(); loop < length; loop++ ) {
			Node node = children.item( loop );

			if ( !( node instanceof Element ) ) {
				continue;
			}

			Element child = (Element) node;

			// ...fix 'class' attribute...

			String clazz = child.getAttribute( typeAttribute );

			if ( clazz != null && !"".equals( clazz ) && clazz.indexOf( StringUtils.SEPARATOR_DOT_CHAR ) == -1 ) {
				child.setAttribute( typeAttribute, packagePrefix + clazz );
			}

			// ...and recurse children

			prependPackageToClassAttribute( child, packagePrefix );
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy