/******************************************************************************* * Copyright (c) 2005, 2006 committers of openArchitectureWare and others. * 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: * Clemens Kadura (zAJKa) - Initial API and implementation *******************************************************************************/ package org.eclipse.emf.mwe.internal.core.ast.parser; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.mwe.core.resources.ResourceLoader; import org.eclipse.emf.mwe.core.resources.ResourceLoaderFactory; public class XmlLocationAnalyser { private Map<String, XmlFileEvaluator> repository = new HashMap<String, XmlFileEvaluator>(); protected ResourceLoader loader; /** * the SAX parser has no exact location handling. The offset and length information are updated here. <br> * TODO: ER: add location of attributes (e.g. for error handling of not existing cartridge file) * * @param loc the (raw) location to be adapted */ public Location adapt(Location loc) { String resource = loc.getResource(); XmlFileEvaluator evaluator = evaluateResource(resource); return evaluator.adapt(loc); } public Location adaptEnd(Location loc) { String resource = loc.getResource(); XmlFileEvaluator evaluator = evaluateResource(resource); return evaluator.adaptEnd(loc); } public int findFirstLineOfTag(String resource, int lineNumber) { if (!repository.containsKey(resource)) { evaluateResource(resource); } return repository.get(resource).findFirstLineOfTag(lineNumber); } private XmlFileEvaluator evaluateResource(String resource) { if (loader == null) loader = ResourceLoaderFactory.createResourceLoader(); InputStream is = loader.getResourceAsStream(resource); XmlFileEvaluator evaluator = new XmlFileEvaluator(); evaluator.evaluate(is); repository.put(resource, evaluator); return evaluator; } protected XmlFileEvaluator newEvaluator() { return new XmlFileEvaluator(); } /** * FOR TEST ONLY */ public XmlFileEvaluator newEvaluator__TEST() { return newEvaluator(); } public class XmlFileEvaluator { private List<XmlTagValues> values = new ArrayList<XmlTagValues>(); // for test only private List<Object[]> result; protected StringBuffer otherText, name, specChar; protected boolean inTag, isClosingTag, inName, inInstruction, inComment = false; public void evaluate(final InputStream is) { if (is == null) return; int c; result = new ArrayList<Object[]>(); int offset = 0, column = 0, nameStart = 0, nameEnd = 0, line = 0, tagLine = 0, firstColumn = 0; reset(); try { while ((c = is.read()) != -1) { switch (c) { case '<': if (!(inTag || inInstruction || inComment)) { inTag = true; tagLine = line; } else { consume(c); } break; case '>': if (inTag) { if (nameEnd == 0) nameEnd = offset - specChar.length(); if (line != tagLine) firstColumn = 0; XmlTagValues value = new XmlTagValues(); value.firstLineOfTag = tagLine + 1; value.lastLineOfTag = line + 1; value.nameStart = nameStart; value.nameEnd = nameEnd; value.firstColumn = firstColumn - (isClosingTag ? 1 : 0); value.endColumn = column + 1; value.name = name.toString(); value.isClosingTag = isClosingTag; values.add(value); result.add(new Object[] { new String[] { name.toString(), otherText.toString() }, value }); reset(); nameEnd = 0; } else if (inInstruction && specChar.toString().endsWith("?")) { reset(); } else if (inComment && specChar.toString().endsWith("--")) { reset(); } else { consume(c); } break; case '/': if (inTag && !inInstruction && name.length() == 0) isClosingTag = true; specChar.append((char) c); break; case '!': if (inTag && !inInstruction && name.length() == 0) { specChar.append((char) c); } else consume(c); break; case '-': if (inTag && name.length() == 0 || inComment) { specChar.append((char) c); } else consume(c); if (specChar.toString().endsWith("!--")) { inTag = false; inInstruction = false; inComment = true; } break; case '?': if (inTag && !inInstruction && name.length() == 0) { inTag = false; inInstruction = true; } else if (inInstruction) { specChar.append((char) c); } else consume(c); break; case ' ': case '\t': if (inName) { inName = false; nameEnd = offset - specChar.length(); } consume(c); break; case '\r': specChar.append((char) c); line++; column = -1; break; case '\n': if (!specChar.toString().endsWith("\r")) { line++; column = -1; } if (inName) { inName = false; nameEnd = offset - specChar.length(); } consume(c); break; default: if (inTag && !inName && name.length() == 0) { specChar = new StringBuffer(); inName = true; nameStart = offset; firstColumn = column; } consume(c); break; } offset++; column++; } } catch (IOException e) { // } } private void consume(int c) { if (inName) name.append(specChar.toString() + (char) c); else if (name.length() > 0) otherText.append(specChar.toString() + (char) c); if (specChar.length() > 0) specChar = new StringBuffer(); } private void reset() { otherText = new StringBuffer(); name = new StringBuffer(); specChar = new StringBuffer(); inTag = false; isClosingTag = false; inName = false; inInstruction = false; inComment = false; } protected int findFirstLineOfTag(int lineNumber) { for (XmlTagValues value : values) { if (lineNumber >= value.firstLineOfTag && lineNumber <= value.lastLineOfTag) return value.firstLineOfTag; } return -1; } public Location adapt(Location rawLoc) { XmlTagValues value = findLine(rawLoc); if (value == null) // should not occur return null; Location loc = new Location(value.firstLineOfTag, 0, rawLoc.getResource()); loc.setNameStart(value.nameStart); loc.setNameEnd(value.nameEnd); return loc; } protected Location adaptEnd(Location rawLoc) { int line = rawLoc.getLineNumber(); int col = rawLoc.getColumnNumber(); String name1 = null; XmlTagValues potentialValue = null; for (XmlTagValues value : values) { if (name1 == null && line == value.firstLineOfTag && col >= value.firstColumn && col <= value.endColumn) name1 = value.name; if (name1 == null && line < value.firstLineOfTag && potentialValue != null) name1 = potentialValue.name; else if (value.name.equals(name1) && value.isClosingTag) { Location loc = new Location(value.firstLineOfTag, 0, rawLoc.getResource()); loc.setNameStart(value.nameStart); loc.setNameEnd(value.nameEnd); return loc; } potentialValue = value; } return null; // no end tag found; can that occur? } protected XmlTagValues findLine(Location loc) { if (values.isEmpty()) return null; int line = loc.getRawLineNumber(); int col = loc.getColumnNumber(); List<XmlTagValues> potentialValues = new ArrayList<XmlTagValues>(); XmlTagValues value = null; for (XmlTagValues nextValue : values) { if (value == null) { value = nextValue; if (line < value.firstLineOfTag) { potentialValues.add(value); break; } continue; } if (line >= value.firstLineOfTag && line < nextValue.firstLineOfTag || value.firstLineOfTag == nextValue.firstLineOfTag) potentialValues.add(value); value = nextValue; // TODO: CK: low: check when we can break } // last tag if (potentialValues.isEmpty() || (value != null && line == value.firstLineOfTag)) potentialValues.add(value); value = handleColumns(col, potentialValues); return value; } protected XmlTagValues handleColumns(int col, List<XmlTagValues> values) { if (values.size() == 1) return values.get(0); XmlTagValues value = null; for (XmlTagValues nextValue : values) { if (value == null) { value = nextValue; if (col < value.firstColumn) return value; continue; } if (col >= value.firstColumn && col < nextValue.firstColumn) return value; value = nextValue; } return value; } public List<Object[]> getResult() { return result; } } protected class XmlTagValues { public int firstLineOfTag; int lastLineOfTag; public int nameStart; public int nameEnd; int firstColumn; int endColumn; boolean isClosingTag; String name; } }