blob: 75e2483f41c4c231ff8d01c6e06fb31e8c1e882d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.wave.pst.model;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.JavaType;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
/**
* Wraps a {@link Descriptor} with methods suitable for stringtemplate.
*
* @author kalman@google.com (Benjamin Kalman)
*/
public final class Message {
private final Descriptor descriptor;
private final String templateName;
private final MessageProperties extraProperties;
// Lazily created.
private List<Field> fields = null;
private List<Message> messages = null;
private List<ProtoEnum> enums = null;
private List<Message> referencedMessages = null;
private List<ProtoEnum> referencedEnums = null;
private String fullName;
private String fullJavaType;
public Message(Descriptor descriptor, String templateName, MessageProperties extraProperties) {
this.descriptor = descriptor;
this.templateName = templateName;
this.extraProperties = extraProperties;
}
/**
* Returns the short name of the Java type of this message, for example:
* <ul>
* <li>org.waveprotocol.pst.examples.Example1.Person = "Person"</li>
* </ul>
*
* @return the name of the protocol buffer message
*/
public String getName() {
return descriptor.getName();
}
/**
* Returns the short name of Java type being generated. For example:
* <ul>
* <li>org.waveprotocol.pst.examples.Example1.Person = <ul>
* <li>"PersonMessage" (for template name "message")</li>
* <li>"PersonMessageServerImpl" (for template name "messageServerImpl")</li></ul>
* </li>
* </ul>
*
* @return the name of the Java message
*/
public String getJavaType() {
return descriptor.getName() + Util.capitalize(templateName);
}
/**
* Returns the full name of the this message in abstract Java space. For
* example:
* <ul>
* <li>org.waveprotocol.pst.examples.Example1.Person = <ul>
* <li>"org.waveprotocol.pst.examples.Person"
* (for template name "Message" and package suffix "dto")</li></ul>
* </li>
* </ul>
*
* @return the name of the protocol buffer message
*/
public String getFullName() {
if (fullName == null) {
fullName = getFullName(false);
}
return fullName;
}
/**
* Returns the full name of the Java type of this message, for example:
* <ul>
* <li>org.waveprotocol.pst.examples.Example1.Person = <ul>
* <li>"org.waveprotocol.pst.examples.dto.PersonMessage"
* (for template name "Message" and package suffix "dto")</li>
* <li>"org.waveprotocol.pst.examples.impl.PersonImpl"
* (for template name "Impl" and package suffix "impl")</li></ul>
* </li>
* </ul>
*
* @return the name of the protocol buffer message
*/
public String getFullJavaType() {
if (fullJavaType == null) {
fullJavaType = getFullName(true);
}
return fullJavaType;
}
/**
* Gets the fully-qualified name of this message.
*
* @param covariant if true, the name refers to the Java type being generated
* for this message. Otherwise, the name refers to a
* template-independent Java type, which may or may not exist. This is
* intended to be used so that the generated Java type for this message
* can refer to other Java types derived from this message.
* @return the fully-qualified name of this message.
*/
private String getFullName(boolean covariant) {
String prefix;
if (descriptor.getContainingType() != null) {
prefix = adapt(descriptor.getContainingType()).getFullName(covariant);
} else {
prefix = covariant ? getPackage() : getPackageBase();
}
return prefix + "." + (covariant ? getJavaType() : getName());
}
/**
* Returns the package of the Java messageas the base plus the suffix
* components of the package, for example given org.waveprotocol.pst.examples.Example1.Person:
* <ul>
* <li>Message = "org.waveprotocol.pst.examples"</li>
* <li>MessageServerImpl (package suffix "server") = "org.waveprotocol.pst.examples.server"</li>
* <li>MessageClientImpl (package suffix "client") = "org.waveprotocol.pst.examples.client"</li>
* </ul>
*
* @return the Java package of the message
*/
public String getPackage() {
String suffix = getPackageSuffix();
return getPackageBase() + (!Strings.isNullOrEmpty(suffix) ? "." + suffix : "");
}
/**
* Returns the base component of the Java message package, for example, given
* org.waveprotocol.pst.examples.Example1.Person:
* <ul>
* <li>Message = "org.waveprotocol.pst.examples"</li>
* <li>MessageServerImpl (package suffix "server") = "org.waveprotocol.pst.examples"</li>
* </ul>
*
* @return the base component of the Java package
*/
public String getPackageBase() {
String javaPackage = descriptor.getFile().getOptions().getJavaPackage();
if (Strings.isNullOrEmpty(javaPackage)) {
javaPackage = descriptor.getFile().getPackage();
}
return javaPackage;
}
/**
* Returns the suffix component of the Java message package, as configured in
* the message's properties file, for example:
* <ul>
* <li>Message = null</li>
* <li>MessageServerImpl = "server"</li>
* <li>MessageClientImpl = "client"</li>
* </ul>
*/
public String getPackageSuffix() {
return extraProperties.getPackageSuffix();
}
/**
* @return the filename of the protocol buffer (.proto) file where the message
* is defined
*/
public String getFilename() {
return descriptor.getFile().getName();
}
/**
* Returns the qualified type of the protobuf message, for example:
* <ul>
* <li>org.waveprotocol.pst.examples.Example1.Person =
* "org.waveprotocol.pst.examples.Example1.Person"</li>
* </ul>
*
* @return the full type of the protocol buffer message
*/
public String getProtoType() {
Deque<String> scopes = Lists.newLinkedList();
for (Descriptor message = descriptor; message != null; message = message.getContainingType()) {
scopes.push(message.getName());
}
scopes.push(descriptor.getFile().getOptions().getJavaOuterClassname());
scopes.push(getPackageBase());
return Joiner.on('.').join(scopes);
}
/**
* @return the fields of the message
*/
public List<Field> getFields() {
if (fields == null) {
ImmutableList.Builder<Field> builder = ImmutableList.builder();
for (FieldDescriptor fd : descriptor.getFields()) {
builder.add(new Field(fd, new Type(fd, templateName, extraProperties), extraProperties));
}
fields = builder.build();
}
return fields;
}
/**
* @return the set of all messages referred to be this message and its nested
* messages. Message references are due to message-typed fields.
*/
public List<Message> getReferencedMessages() {
if (referencedMessages == null) {
referencedMessages = Lists.newArrayList();
for (Descriptor d : collectMessages(descriptor, Sets.<Descriptor>newLinkedHashSet())) {
referencedMessages.add(adapt(d));
}
}
return referencedMessages;
}
/**
* @return the set of all enums referred to be this message and its nested
* messages. Enum references are due to message-typed fields.
*/
public List<ProtoEnum> getReferencedEnums() {
if (referencedEnums == null) {
referencedEnums = Lists.newArrayList();
for (EnumDescriptor d : collectEnums(descriptor, Sets.<EnumDescriptor> newLinkedHashSet())) {
referencedEnums.add(adapt(d));
}
}
return referencedEnums;
}
/**
* Collects messages referred to by a message and its nested messages.
*
* @return {@code referenced}
*/
private static Collection<Descriptor> collectMessages(
Descriptor message, Collection<Descriptor> referenced) {
for (FieldDescriptor fd : message.getFields()) {
if (fd.getJavaType() == JavaType.MESSAGE) {
referenced.add(fd.getMessageType());
}
}
for (Descriptor nd : message.getNestedTypes()) {
collectMessages(nd, referenced);
}
return referenced;
}
/**
* Collects enums referred to by a message and its nested messages.
*
* @return {@code referenced}
*/
private static Collection<EnumDescriptor> collectEnums(
Descriptor d, Collection<EnumDescriptor> referenced) {
for (FieldDescriptor fd : d.getFields()) {
if (fd.getJavaType() == JavaType.ENUM) {
referenced.add(fd.getEnumType());
}
}
for (Descriptor nd : d.getNestedTypes()) {
collectEnums(nd, referenced);
}
return referenced;
}
/**
* @return the nested messages of the message
*/
public List<Message> getNestedMessages() {
if (messages == null) {
ImmutableList.Builder<Message> builder = ImmutableList.builder();
for (Descriptor d : descriptor.getNestedTypes()) {
builder.add(adapt(d));
}
messages = builder.build();
}
return messages;
}
/**
* @return the nested enums of the message
*/
public List<ProtoEnum> getNestedEnums() {
if (enums == null) {
ImmutableList.Builder<ProtoEnum> builder = ImmutableList.builder();
for (EnumDescriptor ed : descriptor.getEnumTypes()) {
builder.add(adapt(ed));
}
enums = builder.build();
}
return enums;
}
/**
* @return whether this is an inner class
*/
public boolean isInner() {
return descriptor.getContainingType() != null;
}
private Message adapt(Descriptor d) {
return new Message(d, templateName, extraProperties);
}
private ProtoEnum adapt(EnumDescriptor d) {
return new ProtoEnum(d, templateName, extraProperties);
}
}