All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.tools.framework.model.ProtoFile Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.api.tools.framework.model;

import com.google.api.tools.framework.model.ExtensionPool.Extension;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Maps;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
import com.google.protobuf.Syntax;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Represents a protocol buffer file.
 */
public class ProtoFile extends ProtoContainerElement {

  private static final Joiner DOT_JOINER = Joiner.on('.');
  // Locations for documentation in proto files.
  private static final ImmutableList FILE_DOC_LOCATIONS = ImmutableList.of(
      "12", // syntax statement
      "8",  // option statement
      "2"   // package statement
      );

  /**
   * Creates a new protocol file backed up by the given descriptor.
   */
  public static ProtoFile create(Model model, FileDescriptorProto proto, boolean isSource,
      ExtensionPool extensionPool) {
    return new ProtoFile(model, proto, isSource, extensionPool);
  }

  // The location path is empty for ProtoFile element.
  private static final String PATH = "";
  private final Model model;
  private final FileDescriptorProto proto;
  private final boolean isSource;
  private final ImmutableList interfaces;
  private final ImmutableListMultimap locationMap;
  private final Map protoToLocation = Maps.newHashMap();
  private final Map extensions = Maps.newHashMap();
  private final Syntax syntax;

  private ProtoFile(Model model, FileDescriptorProto proto, boolean isSource,
      ExtensionPool extensionPool) {
    super(null, proto.getName(), PATH);
    this.model = model;
    this.isSource = isSource;
    this.proto = proto;
    buildChildren(proto.getMessageTypeList(),
        proto.getEnumTypeList(),
        PATH,
        FileDescriptorProto.MESSAGE_TYPE_FIELD_NUMBER,
        FileDescriptorProto.ENUM_TYPE_FIELD_NUMBER,
        extensionPool);

    // Build services.
    ImmutableList.Builder interfacesBuilder = ImmutableList.builder();
    List serviceProtos = proto.getServiceList();
    for (int i = 0; i < serviceProtos.size(); i++) {
      String childPath = buildPath(null, FileDescriptorProto.SERVICE_FIELD_NUMBER, i);
      interfacesBuilder.add(Interface.create(this, serviceProtos.get(i), childPath));
    }

    interfaces = interfacesBuilder.build();

    // Build location map
    ImmutableListMultimap.Builder builder =
        ImmutableListMultimap.builder();
    for (DescriptorProtos.SourceCodeInfo.Location location :
        proto.getSourceCodeInfo().getLocationList()) {
      builder.put(DOT_JOINER.join(location.getPathList()), location);
    }

    // Add all extension locations
    for (Entry entry : extensions.entrySet()) {
      builder.put(entry.getKey().getPath(), entry.getKey().getLocation());
      protoToLocation.put(entry.getValue(), entry.getKey().getFileLocation());
    }
    locationMap = builder.build();

    // Initialize ProtoFile location.
    protoToLocation.put(this, new SimpleLocation(proto.getName()));
    syntax = getProtoSyntax(proto);
  }

  @Override public String toString() {
    return "file " + getSimpleName();
  }

  /**
   * Returns true if this object represents something that is configured as deprecated.
   */
  @Override public boolean isDeprecated() {
    return proto.getOptions().getDeprecated();
  }

  /**
   * Returns true if this file is a proper source, in contrast to a dependency.
   */
  public boolean isSource() {
    return isSource;
  }

  @Override
  public Model getModel() {
    return model;
  }

  @Override
  public String getFullName() {
    return proto.getPackage();
  }

  @Override
  public ProtoFile getFile() {
    return this;
  }

  @Override
  public Location getLocation() {
    return protoToLocation.get(this);
  }

  /**
   * Returns the file descriptor proto.
   */
  public FileDescriptorProto getProto() {
    return proto;
  }

  /**
   * Returns the dependencies.
   */
  public ImmutableList getDependencies() {
    ImmutableList.Builder builder = ImmutableList.builder();
    for (ProtoFile file : model.getFiles()) {
      if (proto.getDependencyList().contains(file.getSimpleName())) {
        builder.add(file);
      }
    }
    return builder.build();
  }

  /**
   * Returns the interfaces in this file.
   */
  public ImmutableList getInterfaces() {
    return interfaces;
  }

  /**
   * Returns the interfaces reachable with active scoper.
   */
  public Iterable getReachableInterfaces() {
    return getModel().reachable(interfaces);
  }

  /**
   * Package private helper to get the location backed up by this proto file for the given element.
   */
  Location getLocation(ProtoElement element) {
    if (protoToLocation.containsKey(element)) {
      return protoToLocation.get(element);
    }

    Location location =
        SimpleLocation.convertFrom(getSourceCodeLocation(element.getPath()), element);
    protoToLocation.put(element, location);
    return location;
  }

  /**
   * Helper to get the documentation backed up by this proto file for the given
   * element.
   */
  public String getDocumentation(ProtoElement element) {
    if (element instanceof ProtoFile) {
      // For files themselves, comments from multiple locations are composed.
      StringBuilder result = new StringBuilder();
      for (String path : FILE_DOC_LOCATIONS) {
        String comment = getDocumentation(path);
        if (Strings.isNullOrEmpty(comment)) {
          continue;
        }
        if (result.length() > 0) {
          result.append('\n');
        }
        result.append(comment);
      }
      return result.toString();
    } else {
      return getDocumentation(element.getPath());
    }
  }

  @Override
  public Syntax getSyntax() {
    return syntax;
  }

  private String getDocumentation(String path) {
    String comment = "";
    DescriptorProtos.SourceCodeInfo.Location location = getSourceCodeLocation(path);
    if (location != null) {
      if (!Strings.isNullOrEmpty(location.getLeadingComments())) {
        comment = location.getLeadingComments();
      }
      if (!Strings.isNullOrEmpty(location.getTrailingComments())){
        comment += location.getTrailingComments();
      }
    }
    return comment;
  }

  private DescriptorProtos.SourceCodeInfo.Location getSourceCodeLocation(String path) {
    if (locationMap.containsKey(path)) {
      // We get the first location.
      return locationMap.get(path).get(0);
    } else {
      return null;
    }
  }

  private static Syntax getProtoSyntax(FileDescriptorProto proto) {
    if (!proto.hasSyntax()) {
      // TODO(user): This can be removed once protoc outputs proto2 when proto2 is being used.
      //     According to liujisi@ it would break a lot of tests, so it is currently not done.
      return Syntax.SYNTAX_PROTO2;
    }
    switch (proto.getSyntax()) {
      case "proto2":
        return Syntax.SYNTAX_PROTO2;
      case "proto3":
        return Syntax.SYNTAX_PROTO3;
      default:
        throw new IllegalArgumentException(
            "Illegal proto syntax for file " + proto.getName() + ": " + proto.getSyntax());
    }
  }

  void addExtension(Extension extension, Field field) {
    extensions.put(extension, field);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy