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

org.openhealthtools.mdht.uml.cda.validate.XPathIndexer Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2010 Sean Muir
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Sean Muir (JKM Software) - initial API and implementation
 *******************************************************************************/
package org.openhealthtools.mdht.uml.cda.validate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

/**
 * XPathIndexer creates and index of line and column number of all ellements
 * within an XML document as supplied by the SAX Locator. The locator is not
 * 100% accurate regarding values but this is currently only used to approximate
 * mark locations as part of the CDA validation within the eclipse Validator2
 * framework.
 * 
 * @author Sean Muir (JKM Software)
 * 
 */
public class XPathIndexer implements ContentHandler {

	/**
	 * ElementEntry is used to maintain a bottom up stack of elements found within the CDA document.
	 * 
	 */
	static private class ElementEntry {

		final String elementName;

		final int elementIndex;

		final ElementEntry elementParent;

		ElementEntry(String elementName, int elementIndex, ElementEntry next) {

			this.elementName = elementName;

			this.elementIndex = elementIndex;

			this.elementParent = next;
		}
	};

	/**
	 * ElementLocationData is used to cache the location of each element
	 * 
	 */
	public static class ElementLocationData {
		public int line;

		public int column;

		public ElementLocationData(int line, int column) {
			super();
			this.line = line;
			this.column = column;
		}
	};

	private ElementEntry currentXMLElement = null;

	/*
	 * ELementIndexes Structure supports [Parent][Element][Location*]
	 * Where
	 * Parent is the xpath to elements location
	 * Element is the element name
	 * Location* is sequential array for location for the named element
	 * 
	 * It is designed to support an incremental count approach of elements so
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * would have one Parent index pointing to a hashmap
	 * in that hash map you have Element index pointing to an array
	 * in the array you would have 3 sequential locations, one for each element found
	 * 
	 * 
	 * The structure and indexElement track element instances to supply running total of element document indexes
	 */
	public HashMap>> elementIndexes = new HashMap>>();

	/*
	 * xpathLocations is a map between xpath and corresponding xml location
	 */
	public HashMap xpathLocations = new HashMap();

	public HashMap xpathAttributes = new HashMap();

	/*
	 * SAX Locator
	 */
	Locator locator = null;

	public void characters(char[] text, int start, int length) throws SAXException {

	}

	public void endDocument() throws SAXException {
	}

	public void endElement(String namespace, String local, String name) throws SAXException {
		currentXMLElement = currentXMLElement.elementParent;
	}

	public void endPrefixMapping(String prefix) throws SAXException {
	}

	public ElementLocationData getAttributeLocationByValue(String value) {

		ElementLocationData elementLocationData = null;

		if (xpathAttributes.containsKey(value)) {
			elementLocationData = xpathAttributes.get(value);
		}
		return elementLocationData;
	}

	/**
	 * getElementLocationByPath
	 * Returns ElementLocationData -
	 * Convert xpath to all upper case to avoid some subtle differences in EMF validation and actual xml content
	 * 
	 */
	public ElementLocationData getElementLocationByPath(String xpath) {

		ElementLocationData elementLocationData = null;

		String upperXPath = xpath.toUpperCase();

		if (xpathLocations.containsKey(upperXPath)) {
			elementLocationData = xpathLocations.get(upperXPath);
		}
		return elementLocationData;
	}

	private String getXPathFromEntry(ElementEntry elementEntry) {

		Stack path = new Stack();

		while (elementEntry != null) {

			path.push("/" + elementEntry.elementName + "[" + elementEntry.elementIndex + "]");

			elementEntry = elementEntry.elementParent;
		}

		String result = "";

		while (!path.isEmpty()) {
			result += path.pop();
		}

		return result;
	}

	public void ignorableWhitespace(char[] text, int start, int length) throws SAXException {
	}

	/*
	 * Populates elementIndexes based on parent and element
	 */
	private int indexElement(String parent, String element) {

		// if new parent, create index
		if (!elementIndexes.containsKey(parent)) {
			elementIndexes.put(parent, new HashMap>());
		}

		// if new element for parent, create index
		if (!elementIndexes.get(parent).containsKey(element)) {
			elementIndexes.get(parent).put(element, new ArrayList());
		}

		// add sequential location based on SAX location
		elementIndexes.get(parent).get(element).add(locator);

		// return index count - used in ElementEntry to recreate xpath
		return elementIndexes.get(parent).get(element).size();
	}

	public void processingInstruction(String target, String data) throws SAXException {
	}

	public void setDocumentLocator(Locator locator) {
		this.locator = locator;
	}

	public void skippedEntity(String name) throws SAXException {
	}

	public void startDocument() throws SAXException {
	}

	public void startElement(String namespace, String local, String name, Attributes attrs) throws SAXException {

		int index = indexElement(getXPathFromEntry(currentXMLElement), name);

		currentXMLElement = new ElementEntry(name, index, currentXMLElement);

		ElementLocationData location = new ElementLocationData(locator.getLineNumber(), locator.getColumnNumber());

		xpathLocations.put(getXPathFromEntry(currentXMLElement).toUpperCase(), location);

		for (int actr = 0; actr < attrs.getLength(); actr++) {

			if (!xpathAttributes.containsKey(attrs.getValue(actr))) {
				xpathAttributes.put(attrs.getValue(actr), location);
			}

		}

	}

	public void startPrefixMapping(String prefix, String uri) throws SAXException {
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy