/******************************************************************************* * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Exadel, Inc. and Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.model.util; import java.util.StringTokenizer; import org.jboss.tools.common.meta.XAttribute; import org.jboss.tools.common.model.XModelObject; import org.jboss.tools.common.model.loaders.XObjectLoader; import org.jboss.tools.common.model.loaders.impl.PropertiesLoader; import org.jboss.tools.common.model.loaders.impl.SimpleWebFileLoader; /** * Searches for position of start tag corresponding to model object. * If, optionally, attribute name is provided, searches for position * of its value. * This feature is necessary because model object does not keep its * position in XML text, being loaded using standard XML parser * that does not provide location in text. * The algorithm used here is not 100% safe, and should not be * used for context changes, rather it can be used for * synchronizing text selection to tree or diagram selection. * * * @author glory */ public class PositionSearcher { String text; XModelObject object; String attribute; int startPosition; int endPosition; boolean selectAttributeName = false; XModelObjectLoaderUtil util; PropertiesLoader propertiesLoader = null; public PositionSearcher() {} public void init(String text, XModelObject object, String attribute) { if(attribute != null && attribute.startsWith("&")) { //$NON-NLS-1$ selectAttributeName = true; attribute = attribute.substring(1); } this.text = text; this.object = object; this.attribute = attribute; XModelObject f = object; while(f != null && f.getFileType() != XModelObject.FILE) f = f.getParent(); if(f != null) { XObjectLoader loader = XModelObjectLoaderUtil.getObjectLoader(f); if(loader instanceof SimpleWebFileLoader) { //TODO have more common case, this does not include WebProcessLoader SimpleWebFileLoader fileLoader = (SimpleWebFileLoader)loader; fileLoader.createRootElement(f); // initializes namespaces if available. util = fileLoader.getUtil(); } else if(loader instanceof PropertiesLoader) { propertiesLoader = (PropertiesLoader)loader; } } } public void execute() { startPosition = -1; endPosition = -1; if(text == null || object == null) return; if(propertiesLoader != null) { String name = object.getAttributeValue("name"); //$NON-NLS-1$ String dname = object.getAttributeValue("dirtyname"); //$NON-NLS-1$ String nvs = object.getAttributeValue("name-value-separator"); //$NON-NLS-1$ int i = text.indexOf(dname + nvs); if(i >= 0) { i = text.indexOf(name, i); startPosition = i; endPosition = i + name.length(); } } else { TagIterator it = new TagIterator(); it.selectObject(object); if(it.startPos < 0 || it.endPos < it.startPos) return; startPosition = it.startPos; endPosition = it.endPos; /// findTagEnd(); selectAttribute(); } } private void selectAttribute() { if(attribute == null || attribute.length() == 0) return; XAttribute a = object.getModelEntity().getAttribute(attribute); if(isESB(a)) { findESBAttrPosition(a); } String xml = (a == null) ? null : a.getXMLName(); if(xml == null || xml.length() == 0) return; if(xml.indexOf(".") < 0) { //$NON-NLS-1$ String s = text.substring(startPosition, endPosition); int i1 = findAttrPosition(s, xml); if(selectAttributeName) { if(i1 < 0) return; startPosition = startPosition + i1; endPosition = startPosition + xml.length(); return; } int i2 = (i1 < 0) ? -1 : s.indexOf('"', i1 + 1); int i3 = (i2 < 0) ? -1 : s.indexOf('"', i2 + 1); if(i3 > 0) { endPosition = startPosition + i3; startPosition = startPosition + i2 + 1; } } else { xml = xml.substring(0, xml.lastIndexOf('.')); //remove .#text if(xml.indexOf('.') >= 0) { xml = xml.substring(xml.lastIndexOf('.') + 1); // simplify - look for the last name only } int e1 = nextStartPosition(text, "</" + getTagXMLName(object) + ">", startPosition); //$NON-NLS-1$ //$NON-NLS-2$ String s = e1 < 0 ? "" : text.substring(startPosition, e1); //$NON-NLS-1$ if(xml.length() == 0) { int i1 = s.indexOf(">"); //$NON-NLS-1$ endPosition = startPosition + i1 + 1; startPosition = startPosition + e1; } else if(s.length() > 0) { int i1a = nextStartPosition(s, "<" + xml + ">", 0); //$NON-NLS-1$ //$NON-NLS-2$ int i1b = nextStartPosition(s, "<" + xml + "/>", 0); //$NON-NLS-1$ //$NON-NLS-2$ int i2 = (i1a < 0) ? -1 : s.indexOf("</", i1a); //$NON-NLS-1$ if(i1a >= 0 && i2 >= 0) { endPosition = startPosition + i2; startPosition = startPosition + i1a + 2 + xml.length(); } else if(i1b >= 0) { endPosition = startPosition + i1b + 3 + xml.length(); startPosition = startPosition + i1b; } } } } private String getTagXMLName(XModelObject object) { String TAG_ATTR = "tag";//$NON-NLS-1$ String result = null; if(object.getModelEntity().getAttribute(TAG_ATTR) != null) { result = object.getAttributeValue(TAG_ATTR); } else { result = object.getModelEntity().getXMLSubPath(); if(result != null && util != null) { result = util.applyNamespaceToTag(result); } } return result; } private int findAttrPosition(String s, String name) { int i = s.indexOf(name); if(i < 0) { return -1; } StringTokenizer st = new StringTokenizer(s, "\"", true); //$NON-NLS-1$ int pos = 0; boolean inValue = false; while(st.hasMoreTokens()) { String t = st.nextToken(); if(t.equals("\"")) { //$NON-NLS-1$ inValue = !inValue; } else { if(!inValue) { int k = t.indexOf(name); if(k >= 0) { boolean ok = true; if(k > 0) { char c = t.charAt(k - 1); if(Character.isJavaIdentifierPart(c) || c == '-') ok = false; } if(ok) return pos + k; } } } pos += t.length(); } return -1; } public int getStartPosition() { return startPosition; } public int getEndPosition() { return endPosition; } void findTagEnd() { if(startPosition < 0) return; if(attribute != null && attribute.length() > 0) return; String tagname = getTagXMLName(object); if(tagname == null || tagname.length() == 0) return; String start = "<" + tagname; //$NON-NLS-1$ if(text.indexOf(start, startPosition) != startPosition) return; String finish = "</" + tagname + ">"; //$NON-NLS-1$ //$NON-NLS-2$ int i = text.indexOf(finish, startPosition); if(i >= 0) endPosition = i + finish.length(); } class TagIterator { int startPos; int endPos; TagIterator() { startPos = -1; endPos = 0; } public void selectObject(XModelObject object) { if(object == null) return; String xml = getTagXMLName(object); String token = "<" + xml; //$NON-NLS-1$ if(object.getFileType() == XModelObject.FILE) { startPos = nextStartPos(token, startPos + 1); if(startPos >= 0) { endPos = text.indexOf(">", startPos + 1); //$NON-NLS-1$ if(endPos < 0) endPos = text.indexOf("<", startPos + 1); //$NON-NLS-1$ if(endPos < 0) endPos = text.length(); else endPos++; } } else if(token.equals("<")) { //$NON-NLS-1$ selectObject(object.getParent()); } else { XModelObject parent = object.getParent(); selectObject(parent); if(startPos < 0) return; // String entity = object.getModelEntity().getName(); XModelObject[] os = parent.getChildren(); for (int i = 0; i < os.length; i++) { String xml_i = getTagXMLName(os[i]); if(!xml.equals(xml_i)) continue; boolean ok = false; while(!ok) { startPos = nextStartPos(token, startPos + 1); if(startPos < 0) return; if(startPos + token.length() == text.length()) { ok = true; } else { char ch = text.charAt(startPos + token.length()); ok = Character.isWhitespace(ch) || ch == '/' || ch == '>'; } } if(os[i] == object) { endPos = text.indexOf(">", startPos + 1); //$NON-NLS-1$ if(endPos < 0) endPos = text.indexOf("<", startPos + 1); //$NON-NLS-1$ if(endPos < 0) endPos = text.length(); else endPos++; return; } } } } int nextStartPos(String token, int b) { return nextStartPosition(text, token, b); } } static int nextStartPosition(String text, String token, int b) { int s = text.indexOf(token, b); if(s < 0) return s; int cb = text.indexOf("<!--", b); //$NON-NLS-1$ if(cb < 0 || cb > s) return s; int ce = text.indexOf("-->", cb); //$NON-NLS-1$ if(ce < 0) return -1; return nextStartPosition(text, token, ce + 3); } /** * Temporal solution; should be refactored to a framework. * */ boolean isESB(XAttribute a) { return a != null && "true".equals(a.getProperty("pre")); } void findESBAttrPosition(XAttribute a) { int ep = text.indexOf("</action>", startPosition); if(ep < 0) return; String dt = text.substring(startPosition, ep); String name = "name=\"" + a.getXMLName() + "\""; int name_i = dt.indexOf(name); if(name_i < 0) return; int ps = dt.lastIndexOf("<property", name_i); if(ps < 0) return; int pe = dt.indexOf("/>", ps); if(pe < 0) return; String dt2 = dt.substring(ps, pe + 2); int value_i = dt2.indexOf("value="); if(value_i >= 0) { int i2 = dt2.indexOf('"', value_i); int i3 = (i2 < 0) ? -1 : dt2.indexOf('"', i2 + 1); if(i3 > i2 + 1) { startPosition = startPosition + ps + i2 + 1; endPosition = startPosition + i3 - i2 - 1; return; } else if(i3 == i2 + 1) { startPosition = startPosition + ps + i2; endPosition = startPosition + 2; return; } } startPosition = startPosition + ps; endPosition = startPosition + pe + 2 - ps; } }