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

com.adobe.xmp.schema.rng.parser.RelaxNGDataModel Maven / Gradle / Ivy

/*
 * File: RelaxNGDataModel.java
 * ************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * ___________________
 *
 *  Copyright 2011 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/

package com.adobe.xmp.schema.rng.parser;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import com.adobe.xmp.schema.model.SchemaDescription;
import org.kohsuke.rngom.rngparser.ast.builder.BuildException;
import org.kohsuke.rngom.rngparser.ast.builder.SchemaBuilder;
import org.kohsuke.rngom.rngparser.ast.util.CheckingSchemaBuilder;
import org.kohsuke.rngom.rngparser.digested.DAnnotation;
import org.kohsuke.rngom.rngparser.digested.DGrammarPattern;
import org.kohsuke.rngom.rngparser.digested.DSchemaBuilderImpl;
import org.kohsuke.rngom.rngparser.parse.IllegalSchemaException;
import org.kohsuke.rngom.rngparser.parse.Parseable;
import org.kohsuke.rngom.rngparser.parse.xml.SAXParseable;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import com.adobe.xmp.schema.model.XMPSchemaException;
import com.adobe.xmp.schema.rng.model.PropertyInfo;
import com.adobe.xmp.schema.rng.model.SchemaInfo;
import com.adobe.xmp.schema.rng.parser.annotation.AnnotationsFactory;
import com.adobe.xmp.schema.rng.parser.annotation.RNGAnnotation;
import com.adobe.xmp.schema.rng.parser.annotation.RNGSchemaAnnotation;
import com.adobe.xmp.schema.rng.parser.exceptions.RNGInvalidSchemaException;
import com.adobe.xmp.schema.rng.parser.exceptions.RNGParseException;
import com.adobe.xmp.schema.rng.parser.generator.XMPSchemaGenerator;
import com.adobe.xmp.schema.rng.parser.traverser.GrammarWalker;

/**
 * This class models a RelaxNG schema's data model as described by RelaxNG spec. This model can describe either a XMP packet composed
 * of multiple XMP schemas or a single XMP schema. This class provides functionality to traverse the RelaxNG data model
 * to construct the XMP schemas represeted by the model.
 * 
 * @author hraghav
 */
@SuppressWarnings("rawtypes")
public class RelaxNGDataModel
{
	private GrammarWalker visitor;

	private DGrammarPattern grammar;

	/**
	 * Construct a RelaxNGDataModel object from the input Grammar pattern.
	 * 
	 * @param grammar
	 * @throws RNGParseException
	 */
	private RelaxNGDataModel(DGrammarPattern grammar) throws RNGParseException
	{
		this.grammar = grammar;
		visitor = new GrammarWalker(grammar);
	}

	/**
	 * Construct a GrammarPattern from the RelaxNG schema which is represented by the inputsource is
	 * 
	 * @param is
	 *            InputSource representing the RelaxNG schema
	 * @param er
	 *            A RelaxNG schema references and includes multiple other files. This enity resolver should define how
	 *            these dependecies should be located by RelaxNG schema parser.
	 * @param eh
	 *            ErrorHandler to handle error call backs generated by RelaxNG schema parser
	 * @return DGrammarPattern
	 * @throws RNGInvalidSchemaException
	 */
	@SuppressWarnings("unchecked")
	static private DGrammarPattern constructGrammar(InputSource is, EntityResolver er, ErrorHandler eh)
			throws RNGInvalidSchemaException
	{
		Parseable p = new SAXParseable(is, eh, er);
		SchemaBuilder sb = new CheckingSchemaBuilder(new DSchemaBuilderImpl(), eh);
		DGrammarPattern grammar = null;
		try
		{
			grammar = (DGrammarPattern) p.parse(sb);
		}
		catch (BuildException e)
		{
			throw new RNGParseException("Exception occured while parsing input RelaxNG schema", e);
		}
		catch (IllegalSchemaException e)
		{
			throw new RNGInvalidSchemaException(e);
		}
		return grammar;
	}

	/**
	 * Construct a RelaxNGDataModel which models the input RelaxNG schema.
	 * 
	 * @param rngFile
	 *            File representing the RelaxNG schema
	 * @param er
	 *            A RelaxNG schema references and includes multiple other files. This enity resolver should define how
	 *            these dependecies should be located by RelaxNG schema parser.
	 * @return RelaxNGDataModel
	 * @throws RNGParseException
	 *             If a exception occured while
	 * @throws RNGInvalidSchemaException
	 *             If the input RelaxNg schema voilates RNG spec
	 * @throws IOException if rngFile cannot be resolved or loaded 
	 * @throws MalformedURLException If URI is malformed
	 */
	static public RelaxNGDataModel newInstance(URI rngFile, EntityResolver er) throws RNGParseException,
		RNGInvalidSchemaException, MalformedURLException, IOException
	{
		// RNGTODO provide a better default error handling or a sample doing the same
		ErrorHandler eh = new DefaultHandler()
		{
			@Override
			public void error(SAXParseException e) throws SAXException
			{
				throw e;
			}
		};
		
		return newInstance(rngFile, er, eh);
	}

	/**
	 * Construct a RelaxNGDataModel which models the input RelaxNG schema.
	 * 
	 * @param rngFile
	 *            File representing the RelaxNG schema
	 * @param er
	 *            A RelaxNG schema references and includes multiple other files. This entity resolver should define how
	 *            these dependencies should be located by RelaxNG schema parser.
	 * @param eh
	 *            ErrorHandler to handle error call backs generated by RelaxNG schema parser
	 * @return RelaxNGDataModel
	 * @throws RNGInvalidSchemaException
	 *             If the input RelaxNg schema violates RNG spec
	 * @throws MalformedURLException If URI is malformed
	 * @throws IOException if rngFile cannot be resolved or loaded 
	 */
	static public RelaxNGDataModel newInstance(URI rngFile, EntityResolver er, ErrorHandler eh)
			throws RNGInvalidSchemaException, MalformedURLException, IOException
	{
		InputSource is = new InputSource(rngFile.toURL().openStream());
		return newInstance(is, er, eh);
	}

	/**
	 * Construct a RelaxNGDataModel which models the input RelaxNG schema.
	 * 
	 * @param is
	 *            InputSource representing the RelaxNG schema
	 * @param er
	 *            A RelaxNG schema references and includes multiple other files. This entity resolver should define how
	 *            these dependencies should be located by RelaxNG schema parser.
	 * @return RelaxNGDataModel
	 * @throws RNGInvalidSchemaException
	 *             If the input RelaxNg schema violates RNG spec
	 */
	static public RelaxNGDataModel newInstance(InputSource is, EntityResolver er)
		throws RNGInvalidSchemaException
	{
		// RNGTODO provide a better default error handling or a sample doing the same
		ErrorHandler eh = new DefaultHandler()
		{
			@Override
			public void error(SAXParseException e) throws SAXException
			{
				throw e;
			}
		};
		
		return newInstance(is, er, eh);
	}
	
	/**
	 * Construct a RelaxNGDataModel which models the input RelaxNG schema.
	 * 
	 * @param is
	 *            InputSource representing the RelaxNG schema
	 * @param er
	 *            A RelaxNG schema references and includes multiple other files. This enity resolver should define how
	 *            these dependecies should be located by RelaxNG schema parser.
	 * @param eh
	 *            ErrorHandler to handle error call backs generated by RelaxNG schema parser
	 * @return RelaxNGDataModel
	 * @throws RNGInvalidSchemaException
	 *             If the input RelaxNg schema voilates RNG spec
	 */
	static public RelaxNGDataModel newInstance(InputSource is, EntityResolver er, ErrorHandler eh)
			throws RNGInvalidSchemaException
	{
		DGrammarPattern grammar = constructGrammar(is, er, eh);
		return new RelaxNGDataModel(grammar);
	}

	/**
	 * Gets the schema which is represented by this RelaxNGDataModel
	 * 
	 * @param handler
	 *            This handler provides notifications for various events such as start, end of parsing a ref, start of
	 *            property parsing, errors recieved while parsing property. This should not be null.
	 * @return First XMP schema from the list of XMP schemas represented by this RelaxNG schema
	 * @throws RNGParseException
	 *             If a problem occured while traversing {@link RelaxNGDataModel} to form a {@link com.adobe.xmp.schema.model.SchemaDescription}
	 *             object. This can happen if the input Relax NG schema does not correctly model a XMP schema.
	 * @throws XMPSchemaException
	 *             Exception thrown for {@link com.adobe.xmp.schema.model.SchemaDescription} is propogated
	 */
	public SchemaDescription constructXMPSchema(SchemaGenerationHandler handler) throws RNGParseException,
			XMPSchemaException
	{
		if (handler == null)
			throw new IllegalArgumentException(
					"ISchemaGenerationTraceHandler used for constructing schemas should not be null");
		visitor.setSchemaGenerationHandler(handler);
		grammar.accept(visitor);

		ArrayList propInfoList = visitor.getPropInfoList();
		if (propInfoList == null || propInfoList.size() == 0)
		{
			throw new RNGParseException("Schema files defined a schema without any properties.");
		}
		
		PropertyInfo firstProperty = propInfoList.get(0);
		SchemaInfo info = firstProperty.getSchemaInfo();
		// Initiate call back for schema construction
		handler.startSchemaConstruction(firstProperty.getNS(), info.getPrefix());
		
		handleAnnotation(info, 0);
		XMPSchemaGenerator rngGenerator = new XMPSchemaGenerator(firstProperty.getNS(), info.getLabel(),
				info.getDescription(), propInfoList);
		return rngGenerator.getModel();
	}

	private void handleAnnotation(SchemaInfo schemaInfo, int index)
	{
		DAnnotation annot = grammar.getAnnotation();
		if (annot == null)
			return;
		List elements = annot.getChildren();
		if (elements == null || index >= elements.size())
			return;
		RNGAnnotation rngAnnot = AnnotationsFactory.createAnnotation(elements.get(index));
		if (rngAnnot == null)
			return;
		if (rngAnnot instanceof RNGSchemaAnnotation)
		{
			((RNGSchemaAnnotation) rngAnnot).setAnnotationData(schemaInfo);
		}
	}
}

/**
 * This class stores a {@link SchemaInfo} object and the list of {@link PropertyInfo} objects it contains
 * 
 * @author hraghav
 */
class SchemaDataHolder
{
	private ArrayList propList;

	private SchemaInfo schemaInfo;

	/**
	 * 
	 * Constructs a new SchemaDataHolder.
	 * 
	 * @param schemaInfo
	 *            {@link SchemaInfo}
	 */
	SchemaDataHolder(SchemaInfo schemaInfo)
	{
		this.schemaInfo = schemaInfo;
		propList = new ArrayList();
	}

	/**
	 * Adds a property to the list of properties
	 * 
	 * @param propInfo
	 *            {@link PropertyInfo} object to add to the list
	 */
	void addPropList(PropertyInfo propInfo)
	{
		if (propInfo != null)
			this.propList.add(propInfo);
	}

	/**
	 * @return The {@link SchemaInfo} object contained in this object
	 */
	SchemaInfo getSchemaInfo()
	{
		return schemaInfo;
	}

	/**
	 * @return List of {@link PropertyInfo} objects contained in this object
	 */
	ArrayList getPropList()
	{
		return propList;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy