Package com.google.eclipse.protobuf.scoping

Source Code of com.google.eclipse.protobuf.scoping.ProtoDescriptor

/*
* 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.scoping;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.io.Closeables.closeQuietly;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE_FIELD__TYPE;
import static com.google.eclipse.protobuf.scoping.OptionType.findOptionTypeForLevelOf;
import static com.google.eclipse.protobuf.util.Encodings.UTF_8;
import static java.util.Collections.*;
import static org.eclipse.xtext.EcoreUtil2.*;
import static org.eclipse.xtext.util.CancelIndicator.NullImpl;

import com.google.common.annotations.VisibleForTesting;
import com.google.eclipse.protobuf.model.util.INodes;
import com.google.eclipse.protobuf.protobuf.*;
import com.google.eclipse.protobuf.protobuf.Enum;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.parser.*;
import org.eclipse.xtext.resource.XtextResource;

import java.io.*;
import java.net.URL;
import java.util.*;

/**
* Contains the elements from descriptor.proto (provided with protobuf's library.)
*
* @author alruiz@google.com (Alex Ruiz)
*/
public class ProtoDescriptor {
  private static final Map<String, OptionType> OPTION_DEFINITION_BY_NAME = newHashMap();

  static {
    populateMap();
  }

  private static void populateMap() {
    for (OptionType type : OptionType.values()) {
      OPTION_DEFINITION_BY_NAME.put(type.messageName(), type);
    }
  }

  private final List<ComplexType> allTypes = newArrayList();
  private final Map<OptionType, Map<String, MessageField>> optionsByType = newHashMap();
  private final Map<String, Enum> enumsByName = newHashMap();

  private Protobuf root;

  private final String importUri;
  private final INodes nodes;
  private final XtextResource resource;

  ProtoDescriptor(String importUri, URI location, IParser parser, INodes nodes) {
    this.importUri = importUri;
    this.nodes = nodes;
    addOptionTypes();
    InputStreamReader reader = null;
    try {
      resource = new XtextResource(location);
      reader = new InputStreamReader(contents(location), UTF_8);
      IParseResult result = parser.parse(reader);
      root = (Protobuf) result.getRootASTElement();
      resource.getContents().add(root);
      resolveLazyCrossReferences(resource, NullImpl);
      initContents();
    } catch (Throwable t) {
      throw new IllegalStateException("Unable to parse descriptor.proto", t);
    } finally {
      closeQuietly(reader);
    }
  }

  /**
   * Returns the contents of the descriptor file at the given location.
   * @param descriptorLocation the location of the descriptor file.
   * @return the contents of the descriptor file.
   * @throws IOException if something goes wrong.
   */
  protected InputStream contents(URI descriptorLocation) throws IOException {
    URL url = new URL(descriptorLocation.toString());
    return url.openConnection().getInputStream();
  }

  private void addOptionTypes() {
    for (OptionType type : OptionType.values()) {
      optionsByType.put(type, new LinkedHashMap<String, MessageField>());
    }
  }

  private void initContents() {
    allTypes.addAll(getAllContentsOfType(root, ComplexType.class));
    for (ComplexType t : allTypes) {
      if (!(t instanceof Message)) {
        continue;
      }
      Message m = (Message) t;
      OptionType type = OPTION_DEFINITION_BY_NAME.get(m.getName());
      if (type == null) {
        continue;
      }
      initOptions(m, type);
    }
  }

  private void initOptions(Message optionGroup, OptionType type) {
    for (MessageElement e : optionGroup.getElements()) {
      if (e instanceof MessageField) {
        addOption((MessageField) e, type);
        continue;
      }
      if (e instanceof Enum) {
        Enum anEnum = (Enum) e;
        String name = anEnum.getName();
        enumsByName.put(name, anEnum);
      }
    }
  }

  private void addOption(MessageField optionSource, OptionType type) {
    if (shouldIgnore(optionSource)) {
      return;
    }
    String name = optionSource.getName();
    optionsByType.get(type).put(name, optionSource);
  }

  private boolean shouldIgnore(MessageField field) {
    return "uninterpreted_option".equals(field.getName());
  }

  /**
   * Returns the options available for the given option or option container. The returned options are defined in
   * {@code google/protobuf/descriptor.proto} (more details can be found <a
   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
   * @param o the given option or option container.
   * @return the options available for the given option or option container, or an empty collection if the are not any
   *         options available.
   */
  public Collection<MessageField> availableOptionsFor(EObject o) {
    EObject target = o;
    if (target instanceof NativeOption) {
      target = target.eContainer();
    }
    OptionType type = findOptionTypeForLevelOf(target);
    if (type == null) {
      return emptyList();
    }
    return optionsOfType(type);
  }

  @VisibleForTesting Collection<MessageField> optionsOfType(OptionType type) {
    return unmodifiableCollection(optionsByType.get(type).values());
  }

  /**
   * Returns the enum type of the given field, only if the given field is defined in
   * {@code google/protobuf/descriptor.proto} and its type is enum (more details can be found <a
   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
   * @param field the given field.
   * @return the enum type of the given field or {@code null} if the type of the given field is not enum.
   */
  public Enum enumTypeOf(MessageField field) {
    if (field == null) {
      return null;
    }
    INode node = nodes.firstNodeForFeature(field, MESSAGE_FIELD__TYPE);
    if (node == null) {
      return null;
    }
    String typeName = node.getText();
    return (isNullOrEmpty(typeName)) ? null : enumByName(typeName.trim());
  }

  @VisibleForTesting Enum enumByName(String qualifiedName) {
    String[] segments = qualifiedName.split("\\.");
    if (segments == null || segments.length == 0) {
      return null;
    }
    return enumsByName.get(segments[segments.length - 1]);
  }

  /**
   * Returns all types in descriptor.proto.
   * @return all types in descriptor.proto.
   */
  public List<ComplexType> allTypes() {
    return unmodifiableList(allTypes);
  }

  public XtextResource resource() {
    return resource;
  }

  /**
   * Returns the URI to use when importing descriptor.proto.
   * @return the URI to use when importing descriptor.proto.
   */
  public String importUri() {
    return importUri;
  }

  @VisibleForTesting MessageField option(String name, OptionType type) {
    Map<String, MessageField> optionByName = optionsByType.get(type);
    return (optionByName != null) ? optionByName.get(name) : null;
  }
}
TOP

Related Classes of com.google.eclipse.protobuf.scoping.ProtoDescriptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.