com.google.eclipse.protobuf.model.util.IndexedElements.java Source code

Java tutorial

Introduction

Here is the source code for com.google.eclipse.protobuf.model.util.IndexedElements.java

Source

/*
 * 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.common.collect.Iterables;
import com.google.common.collect.Range;
import com.google.eclipse.protobuf.model.util.IndexRanges.BackwardsRangeException;
import com.google.eclipse.protobuf.protobuf.FieldOption;
import com.google.eclipse.protobuf.protobuf.Group;
import com.google.eclipse.protobuf.protobuf.IndexRange;
import com.google.eclipse.protobuf.protobuf.IndexedElement;
import com.google.eclipse.protobuf.protobuf.Message;
import com.google.eclipse.protobuf.protobuf.OneOf;
import com.google.eclipse.protobuf.protobuf.Reserved;
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 IndexRanges indexRanges;
    @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 element : elements) {
            if (element instanceof OneOf) {
                maxIndex = max(maxIndex, findMaxIndex(((OneOf) element).getElements()));
            } else if (element instanceof IndexedElement) {
                maxIndex = max(maxIndex, indexOf((IndexedElement) element));
                if (element instanceof Group) {
                    maxIndex = max(maxIndex, findMaxIndex(((Group) element).getElements()));
                }
            } else if (element instanceof Reserved) {
                for (IndexRange indexRange : Iterables.filter(((Reserved) element).getReservations(),
                        IndexRange.class)) {
                    try {
                        Range<Long> range = indexRanges.toLongRange(indexRange);
                        if (range.hasUpperBound()) {
                            maxIndex = max(maxIndex, range.upperEndpoint());
                        }
                    } catch (BackwardsRangeException e) {
                        // Do not consider reserved ranges that are invalid.
                    }
                }
            }
        }

        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;
    }
}