/* * Copyright (c) 2011 Google Inc. * * 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 */ package com.google.eclipse.protobuf.junit.core; import static com.google.eclipse.protobuf.junit.core.SearchOption.IGNORE_CASE; import static com.google.eclipse.protobuf.util.SystemProperties.lineSeparator; import java.util.Iterator; import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.BidiTreeIterator; import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.impl.AbstractNode; import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption; import com.google.eclipse.protobuf.protobuf.Package; /** * @author alruiz@google.com (Alex Ruiz) */ class Finder { // TODO remove feature names that are not used or don't exist any more. private static final String[] FEATURE_NAMES = { "extension", "target", "message", "name", "optionField", "property", "source", "type", "value", "importedNamespace"}; private final String protoAsText; private final AbstractNode root; Finder(INode node, String protoAsText) { this.root = rootOf(node); this.protoAsText = protoAsText; } private static AbstractNode rootOf(INode node) { while (!(node instanceof AbstractNode)) { node = node.getParent(); } return (AbstractNode) node; } <T extends EObject> T find(String text, int count, Class<T> type, List<SearchOption> options) { int offset = protoAsText.indexOf(text); String name = text.substring(0, count); Iterator<AbstractNode> iterator = root.basicIterator(); while (iterator.hasNext()) { AbstractNode node = iterator.next(); int nodeOffset = node.getOffset(); if (nodeOffset > offset || (nodeOffset + node.getLength()) <= offset) { continue; } EObject e = node.getSemanticElement(); if (isDefaultValueFieldOption(name, type, e)) { return type.cast(e); } if (type.isInstance(e)) { if (areNamesEqual(name, e, options)) { return type.cast(e); } } } String format = "Unable to find element. Text: '%s', count: %d, type: %s, options: %s"; throw new AssertionError(String.format(format, text, count, type.getName(), options)); } private boolean isDefaultValueFieldOption(String name, Class<?> type, EObject element) { return "default".equals(name) && type.isInstance(element) && element instanceof DefaultValueFieldOption; } private boolean areNamesEqual(String expected, Object e, List<SearchOption> options) { String realExpected = expected; if (realExpected.indexOf(".") != -1 && !(e instanceof Package)) { String[] segments = expected.split("\\."); QualifiedName qualifiedName = QualifiedName.create(segments); realExpected = qualifiedName.getLastSegment(); } String actual = nameOf(e); if (options.contains(IGNORE_CASE)) { return realExpected.equalsIgnoreCase(actual); } return realExpected.equals(actual); } private String nameOf(Object o) { if (!(o instanceof EObject)) { return null; } EObject e = (EObject) o; for (String name : FEATURE_NAMES) { Object value = feature(e, name); if (value instanceof String) { return (String) value; } if (value != null) { return nameOf(value); } } return null; } private Object feature(EObject e, String featureName) { EStructuralFeature f = e.eClass().getEStructuralFeature(featureName); return (f != null) ? e.eGet(f) : null; } ILeafNode find(String text) { BidiTreeIterator<AbstractNode> iterator = root.basicIterator(); while (iterator.hasNext()) { AbstractNode node = iterator.next(); if (!(node instanceof ILeafNode)) { continue; } String nodeText = clean(node.getText()); if (text.equals(nodeText)) { return (ILeafNode) node; } } String format = "Unable to find node. Text: '%s'"; throw new AssertionError(String.format(format, text)); } private String clean(String text) { return text.replace(lineSeparator(), " ").trim(); } }