blob: 3061b1f130bb2d22e69f66219a7b860c65844d65 [file] [log] [blame]
/*
* 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.model.util;
import static java.lang.Math.max;
import static java.util.Collections.emptyList;
import static org.eclipse.xtext.util.SimpleAttributeResolver.newResolver;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.util.SimpleAttributeResolver;
import com.google.eclipse.protobuf.protobuf.FieldOption;
import com.google.eclipse.protobuf.protobuf.Group;
import com.google.eclipse.protobuf.protobuf.IndexedElement;
import com.google.eclipse.protobuf.protobuf.Message;
import com.google.eclipse.protobuf.protobuf.MessageElement;
import com.google.eclipse.protobuf.protobuf.MessageField;
import com.google.eclipse.protobuf.protobuf.OneOf;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Utility methods related to <code>{@link IndexedElement}</code>s.
*
* @author alruiz@google.com (Alex Ruiz)
*/
@Singleton public class IndexedElements {
private final static SimpleAttributeResolver<EObject, Long> INDEX_RESOLVER = newResolver(long.class, "index");
@Inject private ModelObjects modelObjects;
/**
* Calculates the index value for the given element. The calculated index value is the maximum of all the index values
* of the given element's siblings, plus one. The minimum index value is 1.
* <p>
* For example, in the following message:
* <pre>
* message Person {
* required string name = 1;
* optional string email = 2;
* optional PhoneNumber phone =
* </pre>
* The calculated index value for the element {@code PhoneNumber} will be 3.
* </p>
* @param e the given element.
* @return the calculated value for the index of the given element.
*/
public long calculateNewIndexFor(IndexedElement e) {
EObject containingMessage = EcoreUtil2.getContainerOfType(e, Message.class);
long index = findMaxIndex(containingMessage.eContents());
return index + 1;
}
private long findMaxIndex(Iterable<? extends EObject> elements) {
long maxIndex = 0;
for (EObject e : elements) {
if (e instanceof OneOf) {
maxIndex = max(maxIndex, findMaxIndex(((OneOf) e).getElements()));
} else if (e instanceof IndexedElement) {
maxIndex = max(maxIndex, indexOf((IndexedElement) e));
if (e instanceof Group) {
maxIndex = max(maxIndex, findMaxIndex(((Group) e).getElements()));
}
}
}
return maxIndex;
}
/**
* Returns the index of the given {@link IndexedElement}, or {@code Long.MIN_VALUE} if the given
* {@code IndexedElement} is {@code null}.
*/
public long indexOf(IndexedElement e) {
long index = Long.MIN_VALUE;
EStructuralFeature feature = indexFeatureOf(e);
if (feature != null) {
index = (Long) e.eGet(feature);
}
return index;
}
/**
* Returns the options of the given <code>{@link IndexedElement}</code>.
* @param e the given {@code IndexedElement}.
* @return the options of the given {@code IndexedElement}, or an empty list if the given {@code IndexedElement} is
* {@code null}.
*/
@SuppressWarnings("unchecked")
public List<FieldOption> fieldOptionsOf(IndexedElement e) {
List<FieldOption> options = modelObjects.valueOfFeature(e, "fieldOptions", List.class);
if (options == null) {
options = emptyList();
}
return options;
}
/**
* Sets the index of the given <code>{@link IndexedElement}</code>.
* @param e e the given {@code IndexedElement}.
* @param newIndex the new index to set.
*/
public void setIndexTo(IndexedElement e, long newIndex) {
EStructuralFeature feature = indexFeatureOf(e);
if (feature != null) {
e.eSet(feature, newIndex);
}
}
/**
* Returns the "index" structural feature of the given <code>{@link IndexedElement}</code>.
* @param e the given {@code IndexedElement}.
* @return the "index" structural feature of the given {@code IndexedElement}, or {@code null} if the given
* {@code IndexedElement} is {@code null}.
*/
public EStructuralFeature indexFeatureOf(IndexedElement e) {
return (e != null) ? INDEX_RESOLVER.getAttribute(e) : null;
}
}