package com.orientechnologies.orient.object.jpa.parsing;

import java.net.URL;
import java.util.Collection;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import com.orientechnologies.orient.object.jpa.OJPAPersistenceUnitInfo;

/**
 * This code is responsible for parsing the persistence.xml into PersistenceUnits
 */
public class JPAHandler extends DefaultHandler {
	/** The Persistence Units that we have parsed */
	private final Stack<OJPAPersistenceUnitInfo>	persistenceUnits	= new Stack<OJPAPersistenceUnitInfo>();
	/** root of the persistence unit */
	private final URL															persistenceXmlRoot;
	/** The version of the persistence.xml file */
	private String													xmlSchemaVersion;
	/** The name of the current element */
	private PersistenceXml												element;
	/** A StringBuilder for caching the information from getCharacters */
	private StringBuilder													builder						= new StringBuilder();

	// /**
	// * Create a new JPA Handler for the given persistence.xml. If no xmlSchemaVersion defined it will be parsed
	// */
	// public JPAHandler() {
	// }

	/**
	 * Create a new JPA Handler for the given persistence.xml
	 * 
	 * @param persistenceXmlRoot
	 * @param jpaVersion
	 *          the version of the JPA schema used in the xml
	 */
	public JPAHandler(URL persistenceXmlRoot, JPAVersion jpaVersion) {
		this.persistenceXmlRoot = persistenceXmlRoot;
		this.xmlSchemaVersion = jpaVersion.getVersion();
	}

	@Override
	public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
		// Do this setting first as we use it later.
		element = PersistenceXml.parse((localName == null || localName.isEmpty()) ? name : localName);

		switch (element) {
		case TAG_PERSISTENCE:
			if (xmlSchemaVersion == null) {
				xmlSchemaVersion = PersistenceXmlUtil.parseSchemaVersion(uri, element, attributes);
			}
			break;
		case TAG_PERSISTENCE_UNIT:
			String unitName = attributes.getValue(PersistenceXml.ATTR_UNIT_NAME.toString());
			String transactionType = attributes.getValue(PersistenceXml.ATTR_TRANSACTION_TYPE.toString());
			persistenceUnits.push(new OJPAPersistenceUnitInfo(unitName, transactionType, persistenceXmlRoot, xmlSchemaVersion));
			break;
		case TAG_EXCLUDE_UNLISTED_CLASSES:
			persistenceUnits.peek().setExcludeUnlisted(true);
			break;
		case TAG_PROPERTY:
			persistenceUnits.peek().addProperty(attributes.getValue("name"), attributes.getValue("value"));
			break;
		default:
		}

	}

	@Override
	public void endElement(String uri, String localName, String name) throws SAXException {
		String s = builder.toString().trim();
		// This step is VERY important, otherwise we pollute subsequent
		// ELEMENTS
		builder = new StringBuilder();

		if (s.isEmpty()) {
			return;
		}

		OJPAPersistenceUnitInfo pu = persistenceUnits.peek();

		switch (element) {
		case TAG_PROVIDER:
			pu.setProviderClassName(s);
			break;
		case TAG_JTA_DATA_SOURCE:
			pu.setJtaDataSource(s);
			break;
		case TAG_NON_JTA_DATA_SOURCE:
			pu.setNonJtaDataSource(s);
			break;
		case TAG_MAPPING_FILE:
			pu.addMappingFileName(s);
			break;
		case TAG_JAR_FILE:
			pu.addJarFileName(s);
			break;
		case TAG_CLASS:
			pu.addClassName(s);
			break;
		case TAG_EXCLUDE_UNLISTED_CLASSES:
			pu.setExcludeUnlisted(Boolean.parseBoolean(s));
			break;
		case TAG_SHARED_CACHE_MODE:
			pu.setSharedCacheMode(s);
			break;
		case TAG_VALIDATION_MODE:
			pu.setValidationMode(s);
			break;
		default:
		}
	}

	/**
	 * Collect up the characters, as element's characters may be split across multiple calls. Isn't SAX lovely...
	 */
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		builder.append(ch, start, length);
	}

	/**
	 * We throw this exception to be caught further up and logged as an error there
	 */
	@Override
	public void error(SAXParseException spe) throws SAXException {
		throw spe;
	}

	/**
	 * @return The collection of persistence units that we have parsed
	 */
	public Collection<OJPAPersistenceUnitInfo> getPersistenceUnits() {
		return persistenceUnits;
	}
}
