diff --git a/pom.xml b/pom.xml index aed3f0b0..f8da1e97 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,6 @@ Schema2proto - XSD schema to protobuf. schema2proto-lib - schema2proto-wire schema2proto-maven-plugin schema2proto-xsdproto schema2proto-xsom @@ -59,11 +58,6 @@ pom import - - ${project.groupId} - schema2proto-wire - ${project.version} - ${project.groupId} schema2proto-xsom @@ -189,6 +183,11 @@ wire-runtime-jvm ${wire.version} + + com.squareup.wire + wire-schema-jvm + ${wire.version} + org.assertj assertj-core diff --git a/schema2proto-lib/pom.xml b/schema2proto-lib/pom.xml index aea1743f..1f67041b 100644 --- a/schema2proto-lib/pom.xml +++ b/schema2proto-lib/pom.xml @@ -37,8 +37,12 @@ slf4j-simple - no.entur - schema2proto-wire + com.squareup.wire + wire-schema-jvm + + + com.squareup.okio + okio com.google.protobuf diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/EnumConflictChecker.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/EnumConflictChecker.java index 2d5cefef..6227f8c2 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/EnumConflictChecker.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/EnumConflictChecker.java @@ -36,20 +36,20 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.squareup.wire.schema.EnumConstant; -import com.squareup.wire.schema.EnumType; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.ProtoFile; import no.entur.schema2proto.compatibility.protolock.ProtolockEnum; import no.entur.schema2proto.compatibility.protolock.ProtolockEnumConstant; +import no.entur.schema2proto.wire.MutableEnumConstant; +import no.entur.schema2proto.wire.MutableEnumType; +import no.entur.schema2proto.wire.MutableProtoFile; public class EnumConflictChecker { private final static Logger LOGGER = LoggerFactory.getLogger(EnumConflictChecker.class); private boolean failIfRemovedFieldsTriggered; - public boolean tryResolveEnumConflicts(ProtoFile file, EnumType enumType, ProtolockEnum protolockEnum) { + public boolean tryResolveEnumConflicts(MutableProtoFile file, MutableEnumType enumType, ProtolockEnum protolockEnum) { SortedSet lockEnumConstants = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(protolockEnum.getEnumFields()))); // from // proto.lock SortedSet xsdEnumConstants = Collections.unmodifiableSortedSet( @@ -90,7 +90,7 @@ public boolean tryResolveEnumConflicts(ProtoFile file, EnumType enumType, Protol AtomicInteger nextId = new AtomicInteger(maxLockId + 1); - for (EnumConstant constant : enumType.constants()) { + for (MutableEnumConstant constant : enumType.constants()) { if (!lockNames.contains(constant.getName())) { while (enumType.reserveds().stream().anyMatch(s -> s.matchesTag(nextId.get()))) { nextId.incrementAndGet(); @@ -108,7 +108,7 @@ public boolean tryResolveEnumConflicts(ProtoFile file, EnumType enumType, Protol return failIfRemovedFieldsTriggered; } - private void reserveEnumConstant(ProtoFile file, EnumType e, ProtolockEnumConstant newEnumConstant) { + private void reserveEnumConstant(MutableProtoFile file, MutableEnumType e, ProtolockEnumConstant newEnumConstant) { String reservationDoc = "Reservation added by schema2proto"; Location loc = new Location("", "", 0, 0); @@ -123,7 +123,7 @@ private void reserveEnumConstant(ProtoFile file, EnumType e, ProtolockEnumConsta failIfRemovedFieldsTriggered = true; } - private static Optional getConstant(EnumType e, String intrudingConstantName) { + private static Optional getConstant(MutableEnumType e, String intrudingConstantName) { return e.constants().stream().filter(z -> z.getName().equals(intrudingConstantName)).findFirst(); } diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/FieldConflictChecker.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/FieldConflictChecker.java index 208f38ec..ac097650 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/FieldConflictChecker.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/FieldConflictChecker.java @@ -41,20 +41,20 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.BiMap; -import com.squareup.wire.schema.Field; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.ProtoFile; import no.entur.schema2proto.compatibility.protolock.ProtolockField; import no.entur.schema2proto.compatibility.protolock.ProtolockMessage; +import no.entur.schema2proto.wire.MutableField; +import no.entur.schema2proto.wire.MutableMessageType; +import no.entur.schema2proto.wire.MutableProtoFile; public class FieldConflictChecker { private final Logger LOGGER = LoggerFactory.getLogger(FieldConflictChecker.class); private boolean failIfRemovedFieldsTriggered; - public boolean tryResolveFieldConflicts(ProtoFile file, MessageType protoMessage, ProtolockMessage protolockMessage) { + public boolean tryResolveFieldConflicts(MutableProtoFile file, MutableMessageType protoMessage, ProtolockMessage protolockMessage) { // Compute helper maps SortedSet lockFileFields = Collections.unmodifiableSortedSet(getFields(protolockMessage)); // from proto.lock @@ -84,7 +84,7 @@ public boolean tryResolveFieldConflicts(ProtoFile file, MessageType protoMessage if (originalIdForField == null) { originalIdForField = findNextAvailableFieldNum(protoMessage, protoMessageFieldAsLockFields, lockFileFields).get(); } - Optional intrudingField = getField(protoMessage, protoMessageFieldAsLockField.getName()); + Optional intrudingField = getField(protoMessage, protoMessageFieldAsLockField.getName()); intrudingField.get().updateTag(originalIdForField); // @@ -164,8 +164,8 @@ public boolean tryResolveFieldConflicts(ProtoFile file, MessageType protoMessage } - private void updateFieldTag(AtomicInteger nextAvailableFieldNum, int overlappingId, Optional intrudingField, Optional existingField, - Integer idFromLockFile) { + private void updateFieldTag(AtomicInteger nextAvailableFieldNum, int overlappingId, Optional intrudingField, + Optional existingField, Integer idFromLockFile) { intrudingField.ifPresent(x -> { if (idFromLockFile != null) { x.updateTag(idFromLockFile); @@ -178,7 +178,7 @@ private void updateFieldTag(AtomicInteger nextAvailableFieldNum, int overlapping } @NotNull - private AtomicInteger findNextAvailableFieldNum(MessageType e, SortedSet xsdFields, SortedSet lockFields) { + private AtomicInteger findNextAvailableFieldNum(MutableMessageType e, SortedSet xsdFields, SortedSet lockFields) { AtomicInteger nextAvailableFieldNum = new AtomicInteger( xsdFields.stream().max(Comparator.comparing(ProtolockField::getId)).orElse(new ProtolockField(0, null)).getId() + 1); @@ -207,15 +207,15 @@ private boolean isReserved(ProtolockMessage protolockMessage, ProtolockField fie return reservedFieldNum; } - private Optional getField(MessageType e, String fieldName) { + private Optional getField(MutableMessageType e, String fieldName) { return e.fieldsAndOneOfFields().stream().filter(z -> z.name().equals(fieldName)).findFirst(); } - private Optional getField(MessageType e, Integer fieldId) { + private Optional getField(MutableMessageType e, Integer fieldId) { return e.fieldsAndOneOfFields().stream().filter(z -> z.tag() == fieldId).findFirst(); } - private void reserveField(ProtoFile file, MessageType e, ProtolockField newField) { + private void reserveField(MutableProtoFile file, MutableMessageType e, ProtolockField newField) { String reservationDoc = "Reservation added by schema2proto"; Location loc = new Location("", "", 0, 0); diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/ProtolockBackwardsCompatibilityChecker.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/ProtolockBackwardsCompatibilityChecker.java index 4f272515..857a3df5 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/ProtolockBackwardsCompatibilityChecker.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/compatibility/ProtolockBackwardsCompatibilityChecker.java @@ -33,16 +33,16 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.squareup.wire.schema.EnumType; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.ProtoFile; import no.entur.schema2proto.compatibility.protolock.ProtolockDefinition; import no.entur.schema2proto.compatibility.protolock.ProtolockDefinitions; import no.entur.schema2proto.compatibility.protolock.ProtolockEnum; import no.entur.schema2proto.compatibility.protolock.ProtolockFile; import no.entur.schema2proto.compatibility.protolock.ProtolockMessage; +import no.entur.schema2proto.wire.MutableEnumType; +import no.entur.schema2proto.wire.MutableMessageType; +import no.entur.schema2proto.wire.MutableProtoFile; public class ProtolockBackwardsCompatibilityChecker { @@ -66,7 +66,7 @@ public ProtolockDefinitions getDefinitions() { return definitions; } - private void copyReservations(ProtolockMessage protolockMessage, MessageType protoMessage) { + private void copyReservations(ProtolockMessage protolockMessage, MutableMessageType protoMessage) { if (protolockMessage.getReservedIds() != null && protolockMessage.getReservedIds().length > 0) { Arrays.stream(protolockMessage.getReservedIds()).forEach(reservedId -> protoMessage.addReserved(reservationDoc, reservationLocation, reservedId)); } @@ -75,7 +75,7 @@ private void copyReservations(ProtolockMessage protolockMessage, MessageType pro .forEach(reservedName -> protoMessage.addReserved(reservationDoc, reservationLocation, reservedName)); } - protoMessage.nestedTypes().stream().filter(z -> z instanceof MessageType).map(k -> (MessageType) k).forEach(nestedType -> { + protoMessage.nestedTypes().stream().filter(z -> z instanceof MutableMessageType).map(k -> (MutableMessageType) k).forEach(nestedType -> { if (protolockMessage.getMessages() != null) { Arrays.stream(protolockMessage.getMessages()).forEach(nestedProtolockMessage -> { if (nestedProtolockMessage.getName().equals(nestedType.getName())) { @@ -87,7 +87,7 @@ private void copyReservations(ProtolockMessage protolockMessage, MessageType pro } - private void copyReservations(ProtolockEnum protolockEnum, EnumType protoEnum) { + private void copyReservations(ProtolockEnum protolockEnum, MutableEnumType protoEnum) { LOGGER.debug("Copying reservations for message {} and enum {}", protolockEnum, protoEnum); if (protolockEnum.getReservedIds() != null && protolockEnum.getReservedIds().length > 0) { Arrays.stream(protolockEnum.getReservedIds()).forEach(reservedId -> protoEnum.addReserved(reservationDoc, reservationLocation, reservedId)); @@ -97,7 +97,7 @@ private void copyReservations(ProtolockEnum protolockEnum, EnumType protoEnum) { } } - public boolean resolveBackwardIncompatibilities(ProtoFile protoFile) { + public boolean resolveBackwardIncompatibilities(MutableProtoFile protoFile) { LOGGER.debug("Trying to resolve backward incompabilities in file {}", protoFile); AtomicBoolean failIfRemovedFieldsTriggered = new AtomicBoolean(false); @@ -107,7 +107,7 @@ public boolean resolveBackwardIncompatibilities(ProtoFile protoFile) { if (protolockFile != null) { // For each enum on file level (global enums) - protoFile.types().stream().filter(type -> type instanceof EnumType).map(enumType -> (EnumType) enumType).forEach(enumType -> { + protoFile.types().stream().filter(type -> type instanceof MutableEnumType).map(enumType -> (MutableEnumType) enumType).forEach(enumType -> { if (protolockFile.getEnums() != null) { Arrays.stream(protolockFile.getEnums()).filter(pe -> pe.getName().equals(enumType.name())).findFirst().ifPresent(protolockEnum -> { copyReservations(protolockEnum, enumType); @@ -119,7 +119,7 @@ public boolean resolveBackwardIncompatibilities(ProtoFile protoFile) { } }); - protoFile.types().stream().filter(z -> z instanceof MessageType).map(ke -> (MessageType) ke).forEach(e -> { + protoFile.types().stream().filter(z -> z instanceof MutableMessageType).map(ke -> (MutableMessageType) ke).forEach(e -> { // For each root level message in file ProtolockMessage protolockMessage = getProtolockMessage(protolockFile, e); if (protolockMessage != null) { @@ -134,7 +134,7 @@ public boolean resolveBackwardIncompatibilities(ProtoFile protoFile) { return failIfRemovedFieldsTriggered.get(); } - private boolean resolveBackwardIncompatibilities(ProtoFile protoFile, ProtolockMessage protolockMessage, MessageType protoMessage) { + private boolean resolveBackwardIncompatibilities(MutableProtoFile protoFile, ProtolockMessage protolockMessage, MutableMessageType protoMessage) { LOGGER.debug("Resolving backward compabilities in file {}, message {}", protoFile.name(), protoMessage); AtomicBoolean failIfRemovedFieldsTriggered = new AtomicBoolean(false); @@ -149,7 +149,7 @@ private boolean resolveBackwardIncompatibilities(ProtoFile protoFile, ProtolockM failIfRemovedFieldsTriggered.set(true); } - protoMessage.nestedTypes().stream().filter(type -> type instanceof MessageType).map(r -> (MessageType) r).forEach(nestedProtoMessage -> { + protoMessage.nestedTypes().stream().filter(type -> type instanceof MutableMessageType).map(r -> (MutableMessageType) r).forEach(nestedProtoMessage -> { ProtolockMessage nestedProtolockMessage = getNestedProtolockMessage(protolockMessage, nestedProtoMessage); if (nestedProtolockMessage != null) { if (resolveBackwardIncompatibilities(protoFile, nestedProtolockMessage, nestedProtoMessage)) { @@ -162,11 +162,11 @@ private boolean resolveBackwardIncompatibilities(ProtoFile protoFile, ProtolockM } - private boolean tryResolveEnumConflicts(ProtoFile protoFile, MessageType protoMessage, ProtolockMessage protolockMessage) { + private boolean tryResolveEnumConflicts(MutableProtoFile protoFile, MutableMessageType protoMessage, ProtolockMessage protolockMessage) { LOGGER.debug("Trying to resolve enum conflicts in file {}, message {}", protoFile.name(), protoMessage); AtomicBoolean failIfRemovedFieldsTriggered = new AtomicBoolean(false); // For each enum in proto, try to find mismatching enum values and resolve - protoMessage.nestedTypes().stream().filter(type -> type instanceof EnumType).map(type -> (EnumType) type).forEach(enumType -> { + protoMessage.nestedTypes().stream().filter(type -> type instanceof MutableEnumType).map(type -> (MutableEnumType) type).forEach(enumType -> { // Find matching in protolockmessage if (protolockMessage.getEnums() != null) { Arrays.stream(protolockMessage.getEnums()).filter(e -> e.getName().equals(enumType.name())).findFirst().ifPresent(protolockEnum -> { @@ -180,7 +180,7 @@ private boolean tryResolveEnumConflicts(ProtoFile protoFile, MessageType protoMe return failIfRemovedFieldsTriggered.get(); } - private ProtolockMessage getProtolockMessage(ProtolockFile protolockFile, MessageType protoMessage) { + private ProtolockMessage getProtolockMessage(ProtolockFile protolockFile, MutableMessageType protoMessage) { if (protolockFile != null && protolockFile.getMessages() != null) { return Arrays.stream(protolockFile.getMessages()).filter(message -> message.getName().equals(protoMessage.getName())).findFirst().orElse(null); } @@ -188,7 +188,7 @@ private ProtolockMessage getProtolockMessage(ProtolockFile protolockFile, Messag return null; } - private ProtolockMessage getNestedProtolockMessage(ProtolockMessage protolockMessage, MessageType nestedProtoMessage) { + private ProtolockMessage getNestedProtolockMessage(ProtolockMessage protolockMessage, MutableMessageType nestedProtoMessage) { if (protolockMessage != null && protolockMessage.getMessages() != null) { return Arrays.stream(protolockMessage.getMessages()) .filter(subMessage -> subMessage.getName().equals(nestedProtoMessage.getName())) @@ -199,7 +199,7 @@ private ProtolockMessage getNestedProtolockMessage(ProtolockMessage protolockMes } } - private ProtolockFile getProtolockFile(ProtoFile protoFile) { + private ProtolockFile getProtolockFile(MutableProtoFile protoFile) { String fullPath = protoFile.toString(); if (!fullPath.contains("/")) { // Assume no package in filename yet diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/LocalType.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/LocalType.java index 03e4bb94..dce6fedd 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/LocalType.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/LocalType.java @@ -22,21 +22,22 @@ */ package no.entur.schema2proto.generateproto; -import com.squareup.wire.schema.Field; -import com.squareup.wire.schema.MessageType; import com.sun.xml.xsom.XSComplexType; import com.sun.xml.xsom.XSComponent; +import no.entur.schema2proto.wire.MutableField; +import no.entur.schema2proto.wire.MutableMessageType; + public class LocalType { XSComponent xsComponent; - MessageType localType; - MessageType enclosingType; - Field referencingField; + MutableMessageType localType; + MutableMessageType enclosingType; + MutableField referencingField; String targetPackage; XSComplexType enclosingComplexType; - public LocalType(XSComponent xsComponent, MessageType localType, MessageType enclosingType, Field referencingField, String targetPackage, - XSComplexType enclosingComplexType) { + public LocalType(XSComponent xsComponent, MutableMessageType localType, MutableMessageType enclosingType, MutableField referencingField, + String targetPackage, XSComplexType enclosingComplexType) { this.xsComponent = xsComponent; this.localType = localType; this.enclosingType = enclosingType; diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/ProtoSerializer.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/ProtoSerializer.java index 63d1992a..f755881b 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/ProtoSerializer.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/ProtoSerializer.java @@ -60,17 +60,9 @@ import org.slf4j.LoggerFactory; import com.google.common.base.CaseFormat; -import com.squareup.wire.schema.EnumConstant; -import com.squareup.wire.schema.EnumType; -import com.squareup.wire.schema.Field; +import com.squareup.wire.schema.Field.Label; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.OneOf; -import com.squareup.wire.schema.Options; -import com.squareup.wire.schema.ProtoFile; import com.squareup.wire.schema.Schema; -import com.squareup.wire.schema.SchemaLoader; -import com.squareup.wire.schema.Type; import com.squareup.wire.schema.internal.parser.OptionElement; import com.squareup.wire.schema.internal.parser.OptionElement.Kind; import com.sun.xml.xsom.XSComplexType; @@ -79,6 +71,15 @@ import no.entur.schema2proto.InvalidConfigurationException; import no.entur.schema2proto.compatibility.BackwardsCompatibilityCheckException; import no.entur.schema2proto.compatibility.ProtolockBackwardsCompatibilityChecker; +import no.entur.schema2proto.wire.MutableEnumConstant; +import no.entur.schema2proto.wire.MutableEnumType; +import no.entur.schema2proto.wire.MutableField; +import no.entur.schema2proto.wire.MutableMessageType; +import no.entur.schema2proto.wire.MutableOneOf; +import no.entur.schema2proto.wire.MutableOptions; +import no.entur.schema2proto.wire.MutableProtoFile; +import no.entur.schema2proto.wire.MutableType; +import no.entur.schema2proto.wire.WireSchemaLoader; public class ProtoSerializer { @@ -124,7 +125,7 @@ public ProtoSerializer(Schema2ProtoConfiguration configuration, TypeAndNameMappe } - public void serialize(Map packageToProtoFileMap, List localTypes) throws InvalidXSDException, IOException { + public void serialize(Map packageToProtoFileMap, List localTypes) throws InvalidXSDException, IOException { // Remove temporary generated name suffix replaceGeneratedTypePlaceholder(packageToProtoFileMap, SchemaParser.GENERATED_NAME_PLACEHOLDER, SchemaParser.TYPE_SUFFIX); @@ -212,7 +213,7 @@ public void serialize(Map packageToProtoFileMap, List packageToProtoFileMap, List entry : packageToProtoFileMap.entrySet()) { - ProtoFile protoFile = entry.getValue(); + for (Entry entry : packageToProtoFileMap.entrySet()) { + MutableProtoFile protoFile = entry.getValue(); File destFolder = createPackageFolderStructure(configuration.outputDirectory, protoFile.packageName()); File outputFile = new File(destFolder, protoFile.location().getPath().toLowerCase()); @@ -247,26 +248,26 @@ public void serialize(Map packageToProtoFileMap, List packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void sortFieldsByTag(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { - mt.nestedTypes().stream().filter(MessageType.class::isInstance).forEach(z -> sortFieldsByTag((MessageType) z)); + mt.nestedTypes().stream().filter(MutableMessageType.class::isInstance).forEach(z -> sortFieldsByTag((MutableMessageType) z)); sortFieldsByTag(mt); }); } } - private void sortFieldsByTag(MessageType mt) { - Collections.sort(mt.fields(), Comparator.comparingInt(Field::tag)); - mt.oneOfs().forEach(oneOf -> Collections.sort(oneOf.fields(), Comparator.comparingInt(Field::tag))); + private void sortFieldsByTag(MutableMessageType mt) { + Collections.sort(mt.fields(), Comparator.comparingInt(MutableField::tag)); + mt.oneOfs().forEach(oneOf -> Collections.sort(oneOf.fields(), Comparator.comparingInt(MutableField::tag))); } - private boolean resolveBackwardIncompatibilities(Map packageToProtoFileMap) { + private boolean resolveBackwardIncompatibilities(Map packageToProtoFileMap) { LOGGER.debug("Checking for backward incompatible changes"); AtomicBoolean possibleIncompatibilitiesDetected = new AtomicBoolean(false); - for (ProtoFile file : packageToProtoFileMap.values()) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { if (backwardsCompatibilityChecker.resolveBackwardIncompatibilities(file)) { possibleIncompatibilitiesDetected.set(true); } @@ -276,13 +277,13 @@ private boolean resolveBackwardIncompatibilities(Map packageT return possibleIncompatibilitiesDetected.get(); } - private void sortTypesInProtofile(Map packageToProtoFileMap) { - packageToProtoFileMap.values().forEach(e -> e.types().sort(Comparator.comparing(x -> x.type().simpleName()))); + private void sortTypesInProtofile(Map packageToProtoFileMap) { + packageToProtoFileMap.values().forEach(e -> e.types().sort(Comparator.comparing(x -> x.type().getSimpleName()))); } - private void addPackedOptionToRepeatedFields(Map packageToProtoFileMap, boolean b) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void addPackedOptionToRepeatedFields(Map packageToProtoFileMap, boolean b) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { messageTypes(mt.nestedTypes()).forEach(e -> addPackedOptionToRepeatedFields(packageToProtoFileMap, file, e, b)); addPackedOptionToRepeatedFields(packageToProtoFileMap, file, mt, b); @@ -290,38 +291,42 @@ private void addPackedOptionToRepeatedFields(Map packageToPro } } - private void addPackedOptionToRepeatedFields(Map packageToProtoFileMap, ProtoFile protoFile, MessageType mt, boolean packed) { + private void addPackedOptionToRepeatedFields(Map packageToProtoFileMap, MutableProtoFile protoFile, MutableMessageType mt, + boolean packed) { mt.fields() .stream() - .filter(e -> e.label() == Field.Label.REPEATED) + .filter(e -> e.label() == Label.REPEATED) .filter(e -> isExposedToPackedBug(packageToProtoFileMap, protoFile, e)) .forEach(e -> addPackedOption(e, packed)); } - private void addPackedOption(Field f, boolean packed) { + private void addPackedOption(MutableField f, boolean packed) { OptionElement packedOptionElement = new OptionElement("packed", Kind.BOOLEAN, packed, false); - f.options().getOptionElements().add(packedOptionElement); + f.options().optionElements().add(packedOptionElement); } - private boolean isExposedToPackedBug(Map packageToProtoFileMap, ProtoFile protoFile, Field elementType) { + private boolean isExposedToPackedBug(Map packageToProtoFileMap, MutableProtoFile protoFile, MutableField elementType) { return PACKABLE_SCALAR_TYPES_SET.contains(elementType.getElementType()) || isEnum(elementType, protoFile, packageToProtoFileMap); } - private boolean isEnum(Field elementType, ProtoFile protoFile, Map packageToProtoFileMap) { + private boolean isEnum(MutableField elementType, MutableProtoFile protoFile, Map packageToProtoFileMap) { String packageName = elementType.packageName(); if (packageName != null) { protoFile = packageToProtoFileMap.get(packageName); } - return protoFile.types().stream().filter(EnumType.class::isInstance).anyMatch(e -> ((EnumType) e).name().equals(elementType.getElementType())); + return protoFile.types() + .stream() + .filter(MutableEnumType.class::isInstance) + .anyMatch(e -> ((MutableEnumType) e).name().equals(elementType.getElementType())); } - private void removeUnwantedFields(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void removeUnwantedFields(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> removeUnwantedFields(file, "", mt)); } } - private void removeUnwantedFields(ProtoFile file, final String outerMessagePath, MessageType mt) { + private void removeUnwantedFields(MutableProtoFile file, final String outerMessagePath, MutableMessageType mt) { // Recursive nested type removal StringBuilder nestedPathBuilder = new StringBuilder(); if (!outerMessagePath.equals("")) { @@ -339,20 +344,20 @@ private void removeUnwantedFields(ProtoFile file, final String outerMessagePath, path = outerMessagePath + "."; } - List fieldsToRemove = removeUnwantedFields(file.packageName(), path + mt.getName(), mt.fields()); - for (Field f : fieldsToRemove) { + List fieldsToRemove = removeUnwantedFields(file.packageName(), path + mt.getName(), mt.fields()); + for (MutableField f : fieldsToRemove) { mt.removeDeclaredField(f); String documentation = StringUtils.trimToEmpty(mt.documentation()); documentation += " NOTE: Removed field " + f; mt.updateDocumentation(documentation); } - List oneOfsToRemove = new ArrayList<>(); + List oneOfsToRemove = new ArrayList<>(); - for (OneOf oneOf : mt.oneOfs()) { + for (MutableOneOf oneOf : mt.oneOfs()) { - List oneOfFieldsToRemove = removeUnwantedFields(file.packageName(), path + mt.getName(), oneOf.fields()); - for (Field f : oneOfFieldsToRemove) { + List oneOfFieldsToRemove = removeUnwantedFields(file.packageName(), path + mt.getName(), oneOf.fields()); + for (MutableField f : oneOfFieldsToRemove) { oneOf.fields().remove(f); String documentation = StringUtils.trimToEmpty(mt.documentation()); @@ -367,7 +372,7 @@ private void removeUnwantedFields(ProtoFile file, final String outerMessagePath, } // Remove empty oneOfs - for (OneOf oneOfToRemove : oneOfsToRemove) { + for (MutableOneOf oneOfToRemove : oneOfsToRemove) { mt.removeOneOf(oneOfToRemove); String documentation = StringUtils.trimToEmpty(mt.documentation()); @@ -376,11 +381,11 @@ private void removeUnwantedFields(ProtoFile file, final String outerMessagePath, } } - private List removeUnwantedFields(String packageName, String messageName, List fields) { + private List removeUnwantedFields(String packageName, String messageName, List fields) { - List fieldsToRemove = new ArrayList<>(); + List fieldsToRemove = new ArrayList<>(); - for (Field field : fields) { + for (MutableField field : fields) { if (typeAndFieldNameMapper.ignoreOutputField(packageName, messageName, field.name())) { fieldsToRemove.add(field); } @@ -400,7 +405,7 @@ private File createPackageFolderStructure(File outputDirectory, String packageNa } @NotNull - private String getPathFromPackageNameAndType(String packageName, Type type) { + private String getPathFromPackageNameAndType(String packageName, MutableType type) { String protoFileName = customTypeImportToProtoFile.get(buildFullyQualifiedTypeName(packageName, type)); if (protoFileName != null) { @@ -415,8 +420,8 @@ private String getPathFromPackageName(String packageName) { return packageName.replace('.', '/'); } - private String buildFullyQualifiedTypeName(String packageName, Type type) { - return packageName + "." + type.type().simpleName(); + private String buildFullyQualifiedTypeName(String packageName, MutableType type) { + return packageName + "." + type.type().getSimpleName(); } // Loads imported file and reads all qualified types @@ -431,16 +436,15 @@ private List getFullyQualifiedTypes(String pathName) { } else { File fileToImport = new File(path, pathName); if (fileToImport.exists()) { - SchemaLoader schemaLoader = new SchemaLoader(); + List sources = new ArrayList<>(); for (String importRootFolder : configuration.customImportLocations) { - schemaLoader.addSource(new File(importRootFolder).toPath()); + sources.add(new File(importRootFolder).toPath()); } - schemaLoader.addProto(pathName); - Schema schema = schemaLoader.load(); - ProtoFile customImportFile = schema.protoFile(pathName); + Schema schema = WireSchemaLoader.load(sources, Collections.singletonList(pathName)); + com.squareup.wire.schema.ProtoFile customImportFile = schema.protoFile(pathName); - for (Type type : customImportFile.types()) { - String qualifiedName = buildFullyQualifiedTypeName(customImportFile.packageName(), type); + for (com.squareup.wire.schema.Type type : customImportFile.getTypes()) { + String qualifiedName = customImportFile.getPackageName() + "." + type.getType().getSimpleName(); typeNames.add(qualifiedName); customTypeImportToProtoFile.put(qualifiedName, pathName); @@ -462,27 +466,27 @@ private List getFullyQualifiedTypes(String pathName) { } - private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypeSuffix, String newTypeSuffix) { + private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypeSuffix, String newTypeSuffix) { - for (Entry protoFile : packageToProtoFileMap.entrySet()) { + for (Entry protoFile : packageToProtoFileMap.entrySet()) { replaceGeneratedTypePlaceholder(packageToProtoFileMap, generatedRandomTypeSuffix, newTypeSuffix, protoFile.getValue().types(), protoFile.getValue().packageName()); } } - private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypePlaceholder, String newTypeSuffix, - List types, String packageName) { + private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypePlaceholder, + String newTypeSuffix, List types, String packageName) { Set usedNames = findExistingTypeNamesInProtoFile(types); - for (Type type : types) { + for (MutableType type : types) { // Recurse into nested types replaceGeneratedTypePlaceholder(packageToProtoFileMap, generatedRandomTypePlaceholder, newTypeSuffix, type.nestedTypes(), packageName); - if (type instanceof MessageType) { - MessageType mt = (MessageType) type; + if (type instanceof MutableMessageType) { + MutableMessageType mt = (MutableMessageType) type; replaceGeneratedTypePlaceholder(packageToProtoFileMap, generatedRandomTypePlaceholder, newTypeSuffix, packageName, usedNames, mt); - } else if (type instanceof EnumType) { - EnumType et = (EnumType) type; + } else if (type instanceof MutableEnumType) { + MutableEnumType et = (MutableEnumType) type; String messageName = et.name(); if (messageName.contains(generatedRandomTypePlaceholder)) { String newMessageName = messageName.replaceAll(generatedRandomTypePlaceholder, newTypeSuffix); @@ -498,8 +502,8 @@ private void replaceGeneratedTypePlaceholder(Map packageToPro } } - private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypePlaceholder, String newTypeSuffix, - String packageName, Set usedNames, MessageType mt) { + private void replaceGeneratedTypePlaceholder(Map packageToProtoFileMap, String generatedRandomTypePlaceholder, + String newTypeSuffix, String packageName, Set usedNames, MutableMessageType mt) { String messageName = mt.getName(); if (messageName.contains(generatedRandomTypePlaceholder)) { String newMessageName = messageName.replaceAll(generatedRandomTypePlaceholder, newTypeSuffix); @@ -513,25 +517,25 @@ private void replaceGeneratedTypePlaceholder(Map packageToPro } } - private void uppercaseMessageNames(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void uppercaseMessageNames(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { uppercaseMessageNames(packageToProtoFileMap, file.types(), file.packageName()); } } - private void uppercaseMessageNames(Map packageToProtoFileMap, List types, String packageName) { + private void uppercaseMessageNames(Map packageToProtoFileMap, List types, String packageName) { Set usedNames = findExistingTypeNamesInProtoFile(types); - for (Type type : types) { + for (MutableType type : types) { // Recurse into nested types uppercaseMessageNames(packageToProtoFileMap, type.nestedTypes(), packageName); - if (type instanceof MessageType) { - MessageType mt = (MessageType) type; + if (type instanceof MutableMessageType) { + MutableMessageType mt = (MutableMessageType) type; uppercaseMessageNames(packageToProtoFileMap, packageName, usedNames, mt); - } else if (type instanceof EnumType) { - EnumType et = (EnumType) type; + } else if (type instanceof MutableEnumType) { + MutableEnumType et = (MutableEnumType) type; String messageName = et.name(); String newMessageName = messageName; @@ -584,7 +588,7 @@ private CaseFormat getCaseFormatName(String s) throws IllegalFormatException { throw new IllegalArgumentException(String.format("Couldn't find the case format of the given string '%s'", s)); } - private void uppercaseMessageNames(Map packageToProtoFileMap, String packageName, Set usedNames, MessageType mt) { + private void uppercaseMessageNames(Map packageToProtoFileMap, String packageName, Set usedNames, MutableMessageType mt) { String messageName = mt.getName(); if (!Character.isUpperCase(messageName.charAt(0))) { String newMessageName = StringUtils.capitalize(messageName); @@ -598,38 +602,38 @@ private void uppercaseMessageNames(Map packageToProtoFileMap, } } - private void updateEnumValues(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { - List types = file.types(); + private void updateEnumValues(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { + List types = file.types(); updateEnumValues(types); } } - private void updateEnumValues(List types) { - for (Type t : types) { - if (t instanceof EnumType) { - EnumType e = (EnumType) t; + private void updateEnumValues(List types) { + for (MutableType t : types) { + if (t instanceof MutableEnumType) { + MutableEnumType e = (MutableEnumType) t; updateEnum(e); } updateEnumValues(t.nestedTypes()); } } - private void updateEnum(EnumType e) { + private void updateEnum(MutableEnumType e) { // add UNSPECIFIED value first List optionElementsUnspecified = new ArrayList<>(); // Prefix with enum type name String enumValuePrefix = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, e.name()) + UNDERSCORE; - for (EnumConstant ec : e.constants()) { + for (MutableEnumConstant ec : e.constants()) { String enumValue = escapeEnumValue(ec.getName()); if (enumValue.equalsIgnoreCase("UNSPECIFIED")) { enumValue = "UNSPECIFIED_ENUM_VALUE"; // Handle collision with UNSPECIFIED special value according to Google style guide } ec.updateName(enumValuePrefix + enumValue); } - EnumConstant unspecified = new EnumConstant(new Location("", "", 0, 0), enumValuePrefix + "UNSPECIFIED", 0, "Default", - new Options(Options.ENUM_VALUE_OPTIONS, optionElementsUnspecified)); + MutableEnumConstant unspecified = new MutableEnumConstant(new Location("", "", 0, 0), enumValuePrefix + "UNSPECIFIED", 0, "Default", + new MutableOptions(MutableOptions.ENUM_VALUE_OPTIONS, optionElementsUnspecified)); e.constants().add(0, unspecified); } @@ -670,30 +674,29 @@ private String escapeEnumValue(String name) { } private void parseWrittenFiles() throws IOException { - SchemaLoader schemaLoader = new SchemaLoader(); - try { + List sources = new ArrayList<>(); + for (String importRootFolder : configuration.customImportLocations) { + sources.add(new File(importRootFolder).toPath()); + } + sources.add(configuration.outputDirectory.toPath()); + + List protos = new ArrayList<>(); if (configuration.includeValidationRules) { - schemaLoader.addProto(VALIDATION_PROTO_IMPORT); + protos.add(VALIDATION_PROTO_IMPORT); } if (configuration.includeXsdOptions) { - schemaLoader.addProto(XSDOPTIONS_PROTO_IMPORT); + protos.add(XSDOPTIONS_PROTO_IMPORT); } - for (String importRootFolder : configuration.customImportLocations) { - schemaLoader.addSource(new File(importRootFolder).toPath()); - } - - schemaLoader.addSource(configuration.outputDirectory); - - for (Path s : schemaLoader.sources()) { + for (Path s : sources) { LOGGER.debug("Linking proto from path {}", s); } - for (String s : schemaLoader.protos()) { + for (String s : protos) { LOGGER.debug("Linking proto {}", s); } - schemaLoader.load(); + WireSchemaLoader.load(sources, protos); } catch (IOException e) { throw new ConversionException("Parsing of written output failed, the proto files are not valid", e); } @@ -711,17 +714,17 @@ private void parseWrittenFiles() throws IOException { * * @Override public Iterator iterator() { return iterator; } }; } */ - private void computeFilenames(Map packageToProtoFileMap) { - for (Entry protoFile : packageToProtoFileMap.entrySet()) { - ProtoFile file = protoFile.getValue(); + private void computeFilenames(Map packageToProtoFileMap) { + for (Entry protoFile : packageToProtoFileMap.entrySet()) { + MutableProtoFile file = protoFile.getValue(); String filename = protoFile.getKey().replaceAll("\\.", UNDERSCORE) + ".proto"; Location loc = new Location("", filename, 0, 0); file.setLocation(loc); } } - private void addConfigurationSpecifiedOptions(Map packageToProtoFileMap) { - for (Entry protoFile : packageToProtoFileMap.entrySet()) { + private void addConfigurationSpecifiedOptions(Map packageToProtoFileMap) { + for (Entry protoFile : packageToProtoFileMap.entrySet()) { for (Entry option : configuration.options.entrySet()) { Kind kind = null; @@ -739,10 +742,10 @@ private void addConfigurationSpecifiedOptions(Map packageToPr } } - private void includeGoPackageNameOptions(Map packageToProtoFileMap) { - for (ProtoFile protoFile : packageToProtoFileMap.values()) { + private void includeGoPackageNameOptions(Map packageToProtoFileMap) { + for (MutableProtoFile protoFile : packageToProtoFileMap.values()) { String optionName = "go_package"; - boolean alreadySet = protoFile.options().getOptionElements().stream().anyMatch(existingOption -> optionName.equals(existingOption.getName())); + boolean alreadySet = protoFile.options().optionElements().stream().anyMatch(existingOption -> optionName.equals(existingOption.getName())); if (!alreadySet) { String goPackageName = packageNameToGoPackageName(configuration.goPackageSourcePrefix, protoFile.packageName()); OptionElement optionElement = new OptionElement(optionName, OptionElement.Kind.STRING, goPackageName, false); @@ -751,10 +754,10 @@ private void includeGoPackageNameOptions(Map packageToProtoFi } } - private void resolveRecursiveImports(Map packageToProtoFileMap) { + private void resolveRecursiveImports(Map packageToProtoFileMap) { Map> imports = new HashMap<>(); - for (ProtoFile file : packageToProtoFileMap.values()) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { List fileImports = new ArrayList<>(); fileImports.addAll(file.imports()); @@ -765,12 +768,12 @@ private void resolveRecursiveImports(Map packageToProtoFileMa imports.put(file.toString(), fileImports); } - for (Entry protoFileEntry : packageToProtoFileMap.entrySet()) { - ProtoFile protoFile = protoFileEntry.getValue(); + for (Entry protoFileEntry : packageToProtoFileMap.entrySet()) { + MutableProtoFile protoFile = protoFileEntry.getValue(); String filename = protoFile.location().getPath(); if (hasRecursiveImports(imports, filename, filename)) { LOGGER.error("File {} recursively imports itself.", filename); - // TODO: Extract affected types to a separate, common ProtoFile + // TODO: Extract affected types to a separate, common MutableProtoFile } } @@ -799,13 +802,13 @@ private boolean hasRecursiveImports(Map> imports, String ro return false; } - private void addConfigurationSpecifiedImports(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void addConfigurationSpecifiedImports(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { for (String customImport : configuration.customImports) { boolean customImportInUse = false; List qualifiedTypes = getFullyQualifiedTypes(customImport); - typeloop: for (Type type : file.types()) { + typeloop: for (MutableType type : file.types()) { for (String qualifiedType : qualifiedTypes) { customImportInUse = isCustomImportInUseInNestedTypes(qualifiedType, type); if (customImportInUse) { @@ -828,18 +831,18 @@ private void addConfigurationSpecifiedImports(Map packageToPr } } - private boolean isCustomImportInUseInNestedTypes(String importPackage, Type type) { + private boolean isCustomImportInUseInNestedTypes(String importPackage, MutableType type) { AtomicBoolean customImportInUse = new AtomicBoolean(false); - if (type instanceof MessageType) { - for (Field field : ((MessageType) type).fieldsAndOneOfFields()) { + if (type instanceof MutableMessageType) { + for (MutableField field : ((MutableMessageType) type).fieldsAndOneOfFields()) { if (field.getElementType() != null && field.getElementType().equalsIgnoreCase(importPackage)) { customImportInUse.set(true); } } if (!customImportInUse.get()) { messageTypes(type.nestedTypes()).forEach(mt -> { - for (Field field : ((MessageType) type).fieldsAndOneOfFields()) { + for (MutableField field : ((MutableMessageType) type).fieldsAndOneOfFields()) { if (field.getElementType() != null && field.getElementType().equalsIgnoreCase(importPackage)) { customImportInUse.set(true); } @@ -856,8 +859,8 @@ private boolean isCustomImportInUseInNestedTypes(String importPackage, Type type return customImportInUse.get(); } - private void computeLocalImports(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void computeLocalImports(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { SortedSet imports = new TreeSet<>(file.imports()); messageTypes(file.types()).forEach(mt -> computeLocalImports(packageToProtoFileMap, file, imports, mt)); @@ -868,16 +871,17 @@ private void computeLocalImports(Map packageToProtoFileMap) { } } - private void computeLocalImports(Map packageToProtoFileMap, ProtoFile file, SortedSet imports, MessageType messageType) { + private void computeLocalImports(Map packageToProtoFileMap, MutableProtoFile file, SortedSet imports, + MutableMessageType messageType) { messageTypes(messageType.nestedTypes()).forEach(e -> computeLocalImports(packageToProtoFileMap, file, imports, e)); - for (Field field : messageType.fieldsAndOneOfFields()) { + for (MutableField field : messageType.fieldsAndOneOfFields()) { String packageName = StringUtils.trimToNull(field.packageName()); if (file.packageName() != null && file.packageName().equals(packageName)) { field.clearPackageName(); } else if (packageName != null) { // Add import - ProtoFile fileToImport = packageToProtoFileMap.get(packageName); + MutableProtoFile fileToImport = packageToProtoFileMap.get(packageName); if (fileToImport != null) { imports.add(getPathFromPackageNameAndType(packageName, messageType) + "/" + fileToImport.location().getPath()); } else { @@ -887,20 +891,20 @@ private void computeLocalImports(Map packageToProtoFileMap, P } } - private void moveFieldPackageNameToFieldTypeName(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void moveFieldPackageNameToFieldTypeName(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(this::moveFieldPackageNameToFieldTypeName); } } // Recursively loops through all fields for all nested types - private void moveFieldPackageNameToFieldTypeName(MessageType messageType) { + private void moveFieldPackageNameToFieldTypeName(MutableMessageType messageType) { messageTypes(messageType.nestedTypes()).forEach(this::moveFieldPackageNameToFieldTypeName); - List fields = messageType.fieldsAndOneOfFields(); + List fields = messageType.fieldsAndOneOfFields(); - for (Field field : fields) { + for (MutableField field : fields) { String fieldPackageName = StringUtils.trimToNull(field.packageName()); if (fieldPackageName != null) { field.clearPackageName(); @@ -912,11 +916,11 @@ private void moveFieldPackageNameToFieldTypeName(MessageType messageType) { /* * Adds leading '.' to field.elementType when needed. Ref.: https://developers.google.com/protocol-buffers/docs/proto3#packages-and-name-resolution */ - private void addLeadingPeriodToElementType(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void addLeadingPeriodToElementType(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { // TODO must this be done for nested types as well or handled differently? - for (Field field : mt.fieldsAndOneOfFields()) { + for (MutableField field : mt.fieldsAndOneOfFields()) { String fieldElementType = StringUtils.trimToNull(field.getElementType()); if (fieldElementType != null && fieldElementType.contains(".")) { for (String pkg : packageToProtoFileMap.keySet()) { @@ -934,18 +938,18 @@ private void addLeadingPeriodToElementType(Map packageToProto } } - private void translateTypes(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void translateTypes(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { translateTypes(packageToProtoFileMap, file.types(), file.packageName()); } } - private void translateTypes(Map packageToProtoFileMap, List types, String packageName) { + private void translateTypes(Map packageToProtoFileMap, List types, String packageName) { if (!types.isEmpty()) { Set usedNames = findExistingTypeNamesInProtoFile(types); - for (Type type : types) { - if (type instanceof MessageType) { - MessageType mt = (MessageType) type; + for (MutableType type : types) { + if (type instanceof MutableMessageType) { + MutableMessageType mt = (MutableMessageType) type; translateTypes(packageToProtoFileMap, type.nestedTypes(), packageName); @@ -964,8 +968,8 @@ private void translateTypes(Map packageToProtoFileMap, List packageToProtoFileMap, List fields) { - for (Field field : fields) { + private void translateTypes(List fields) { + for (MutableField field : fields) { // Translate basic types as well if (field.packageName() == null && basicTypes.contains(field.getElementType())) { String newFieldType = typeAndFieldNameMapper.translateType(field.getElementType()); @@ -997,13 +1001,13 @@ private void translateTypes(List fields) { } } - private void replaceTypes(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void replaceTypes(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { replaceTypesRecursive(file.types()); } } - private void replaceTypesRecursive(List types) { + private void replaceTypesRecursive(List types) { messageTypes(types).forEach(mt -> { replaceTypesRecursive(mt.nestedTypes()); replaceTypes(mt.fieldsAndOneOfFields()); @@ -1011,33 +1015,33 @@ private void replaceTypesRecursive(List types) { } - private void replaceTypes(List fields) { - for (Field field : fields) { + private void replaceTypes(List fields) { + for (MutableField field : fields) { String newFieldType = typeAndFieldNameMapper.replaceType(field.getElementType()); field.updateElementType(newFieldType); } } - private void updateTypeReferences(Map packageToProtoFileMap, String packageNameOfType, String oldName, String newName) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void updateTypeReferences(Map packageToProtoFileMap, String packageNameOfType, String oldName, String newName) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { updateTypeReferences(packageNameOfType, oldName, newName, file.types(), file.packageName()); } } - private void updateTypeReferences(String packageNameOfType, String oldName, String newName, List types, String currentMessageTypePackage) { - for (Type type : types) { + private void updateTypeReferences(String packageNameOfType, String oldName, String newName, List types, String currentMessageTypePackage) { + for (MutableType type : types) { updateTypeReferences(packageNameOfType, oldName, newName, type.nestedTypes(), currentMessageTypePackage); - if (type instanceof MessageType) { - MessageType mt = (MessageType) type; + if (type instanceof MutableMessageType) { + MutableMessageType mt = (MutableMessageType) type; updateTypeReferences(packageNameOfType, oldName, newName, mt, mt.fieldsAndOneOfFields(), currentMessageTypePackage); } } } - private void updateTypeReferences(String packageNameOfType, String oldName, String newName, MessageType mt, Collection fields, + private void updateTypeReferences(String packageNameOfType, String oldName, String newName, MutableMessageType mt, Collection fields, String currentMessageTypePackage) { - for (Field field : fields) { + for (MutableField field : fields) { if (samePackage(field.packageName(), packageNameOfType)) { String fieldType = field.getElementType(); if (fieldType.equals(oldName)) { @@ -1047,25 +1051,25 @@ private void updateTypeReferences(String packageNameOfType, String oldName, Stri } } - Options options = mt.options(); + MutableOptions options = mt.options(); // Avoid concurrent mod exception - List listCopy = new ArrayList<>(options.getOptionElements()); + List listCopy = new ArrayList<>(options.optionElements()); - listCopy.stream().filter(e -> e.getName().equals(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME)).forEach(e -> { + listCopy.stream().filter(e -> e.getName().equals(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME)).forEach(e -> { String packageAndType = (String) e.getValue(); if (packageAndType.equals(".")) { String packageName = packageAndType.substring(0, packageAndType.lastIndexOf('.')); String messageName = packageAndType.substring(packageName.hashCode() + 1); if (packageName.equals(packageAndType) && oldName.equals(messageName)) { - options.replaceOption(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, - new OptionElement(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, Kind.STRING, packageName + "." + newName, true)); + options.replaceOption(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, + new OptionElement(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, Kind.STRING, packageName + "." + newName, true)); } } else if (currentMessageTypePackage.equals(packageNameOfType) && packageAndType.equals(oldName)) { - options.replaceOption(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, - new OptionElement(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, Kind.STRING, newName, true)); + options.replaceOption(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, + new OptionElement(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME, Kind.STRING, newName, true)); } }); @@ -1083,21 +1087,21 @@ private boolean samePackage(String packageNameOfFile, String packageNameOfRefere } } - private Set findExistingTypeNamesInProtoFile(List types) { + private Set findExistingTypeNamesInProtoFile(List types) { Set existingTypeNames = new HashSet<>(); - for (Type t : types) { - if (t instanceof MessageType) { - existingTypeNames.add(((MessageType) t).getName()); - } else if (t instanceof EnumType) { - existingTypeNames.add(((EnumType) t).name()); + for (MutableType t : types) { + if (t instanceof MutableMessageType) { + existingTypeNames.add(((MutableMessageType) t).getName()); + } else if (t instanceof MutableEnumType) { + existingTypeNames.add(((MutableEnumType) t).name()); } } return existingTypeNames; } - private void translateFieldNames(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void translateFieldNames(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { messageTypes(mt.nestedTypes()).forEach(e -> translateFieldNames(e.fieldsAndOneOfFields())); @@ -1108,16 +1112,16 @@ private void translateFieldNames(Map packageToProtoFileMap) { } } - private void translateFieldNames(List fields) { - for (Field field : fields) { + private void translateFieldNames(List fields) { + for (MutableField field : fields) { String fieldName = field.name(); String newFieldName = typeAndFieldNameMapper.translateFieldName(fieldName); field.updateName(newFieldName); } } - private void handleFieldNameCaseInsensitives(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void handleFieldNameCaseInsensitives(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { @@ -1130,10 +1134,10 @@ private void handleFieldNameCaseInsensitives(Map packageToPro } } - private void handleFieldNameCaseInsensitives(List fields) { + private void handleFieldNameCaseInsensitives(List fields) { Set fieldNamesUppercase = new HashSet<>(); - for (Field field : fields) { + for (MutableField field : fields) { String fieldName = field.name(); boolean existedBefore = fieldNamesUppercase.add(fieldName.toUpperCase()); if (!existedBefore) { @@ -1143,17 +1147,17 @@ private void handleFieldNameCaseInsensitives(List fields) { } } - private void underscoreFieldNames(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void underscoreFieldNames(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(this::underscoreFieldNames); } } - private void underscoreFieldNames(MessageType mt) { + private void underscoreFieldNames(MutableMessageType mt) { messageTypes(mt.nestedTypes()).forEach(this::underscoreFieldNames); - for (Field field : mt.fieldsAndOneOfFields()) { + for (MutableField field : mt.fieldsAndOneOfFields()) { String fieldName = field.name(); boolean startsWithUnderscore = fieldName.startsWith(UNDERSCORE); boolean endsWithUnderscore = fieldName.endsWith(UNDERSCORE); @@ -1178,8 +1182,8 @@ private void underscoreFieldNames(MessageType mt) { } - private void escapeReservedJavaKeywords(Map packageToProtoFileMap) { - for (ProtoFile file : packageToProtoFileMap.values()) { + private void escapeReservedJavaKeywords(Map packageToProtoFileMap) { + for (MutableProtoFile file : packageToProtoFileMap.values()) { messageTypes(file.types()).forEach(mt -> { messageTypes(mt.nestedTypes()).forEach(this::escapeReservedJavaKeywords); escapeReservedJavaKeywords(mt); @@ -1188,8 +1192,8 @@ private void escapeReservedJavaKeywords(Map packageToProtoFil } } - private void escapeReservedJavaKeywords(MessageType mt) { - for (Field field : mt.fieldsAndOneOfFields()) { + private void escapeReservedJavaKeywords(MutableMessageType mt) { + for (MutableField field : mt.fieldsAndOneOfFields()) { String fieldName = field.name(); String newFieldName = typeAndFieldNameMapper.escapeFieldName(fieldName); field.updateName(newFieldName); @@ -1233,12 +1237,12 @@ public String toString() { } } - private void moveReusedLocalTypesToGlobal(Map packageToProtoFileMap, List localTypesAllPackages) { + private void moveReusedLocalTypesToGlobal(Map packageToProtoFileMap, List localTypesAllPackages) { LOGGER.debug("Reorganizing embedded local types into global if reused"); localTypesAllPackages.forEach(e -> LOGGER.debug("{} -> {}", e.enclosingType.getName(), e.localType.getName())); - for (Entry protoFileEntry : packageToProtoFileMap.entrySet()) { + for (Entry protoFileEntry : packageToProtoFileMap.entrySet()) { String currentPackageName = protoFileEntry.getKey(); @@ -1289,7 +1293,7 @@ private void moveReusedLocalTypesToGlobal(Map packageToProtoF if (packageName == null) { packageName = Schema2ProtoConfiguration.DEFAULT_PROTO_PACKAGE; } - ProtoFile enclosingFile = packageToProtoFileMap.get(packageName); + MutableProtoFile enclosingFile = packageToProtoFileMap.get(packageName); // DuplicateCheck String candidateName = currentComponent.messageName; @@ -1302,15 +1306,15 @@ private void moveReusedLocalTypesToGlobal(Map packageToProtoF first.localType.getName(), ToStringBuilder.reflectionToString(enclosingTypes.toArray(), ToStringStyle.SIMPLE_STYLE))); } candidateName = StringUtils.capitalize(enclosingTypes.iterator().next()) + "_" - + StringUtils.capitalize(first.localType.type().simpleName()); + + StringUtils.capitalize(first.localType.type().getSimpleName()); LOGGER.debug("Candidate name for inherited local type prefixed with enclosing type {}", candidateName); } else { LOGGER.debug("Candidate name for inherited local type {} is unique - keeping as is", candidateName); } - Optional existingType = checkForExistingType(enclosingFile, candidateName); + Optional existingType = checkForExistingType(enclosingFile, candidateName); if (!existingType.isPresent()) { - MessageType localToBecomeGlobal = first.localType; + MutableMessageType localToBecomeGlobal = first.localType; localToBecomeGlobal.updateName(candidateName); enclosingFile.types().add(localToBecomeGlobal); @@ -1336,10 +1340,10 @@ private void moveReusedLocalTypesToGlobal(Map packageToProtoF } @NotNull - private Optional checkForExistingType(ProtoFile enclosingFile, String candidateName) { - Optional existingType = Optional.empty(); - for (Type type : enclosingFile.types()) { - if (candidateName.equals(type.type().simpleName())) { + private Optional checkForExistingType(MutableProtoFile enclosingFile, String candidateName) { + Optional existingType = Optional.empty(); + for (MutableType type : enclosingFile.types()) { + if (candidateName.equals(type.type().getSimpleName())) { existingType = Optional.of(type); break; } @@ -1347,8 +1351,8 @@ private Optional checkForExistingType(ProtoFile enclosingFile, String cand return existingType; } - private Stream messageTypes(List types) { - return types.stream().filter(MessageType.class::isInstance).map(MessageType.class::cast); + private Stream messageTypes(List types) { + return types.stream().filter(MutableMessageType.class::isInstance).map(MutableMessageType.class::cast); } } diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/Schema2Proto.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/Schema2Proto.java index 69c5c541..7dbe6139 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/Schema2Proto.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/Schema2Proto.java @@ -53,9 +53,8 @@ import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.error.YAMLException; -import com.squareup.wire.schema.ProtoFile; - import no.entur.schema2proto.InvalidConfigurationException; +import no.entur.schema2proto.wire.MutableProtoFile; public class Schema2Proto { private static final String OPTION_OUTPUT_DIRECTORY = "outputDirectory"; @@ -110,7 +109,7 @@ public static void parseAndSerialize(Schema2ProtoConfiguration configuration) th SchemaParser xp = new SchemaParser(configuration); LOGGER.info("Starting to parse {}", configuration.xsdFile); - Map packageToFiles = xp.parse(); + Map packageToFiles = xp.parse(); List localTypes = xp.getLocalTypes(); TypeAndNameMapper pbm = new TypeAndNameMapper(configuration); diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/SchemaParser.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/SchemaParser.java index 340d7eb2..fbb5f9cf 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/SchemaParser.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/generateproto/SchemaParser.java @@ -22,7 +22,7 @@ */ package no.entur.schema2proto.generateproto; -import static com.squareup.wire.schema.MessageType.XSD_MESSAGE_OPTIONS_PACKAGE; +import static no.entur.schema2proto.wire.MutableMessageType.XSD_MESSAGE_OPTIONS_PACKAGE; import java.io.IOException; import java.net.URI; @@ -53,18 +53,10 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; -import com.squareup.wire.schema.EnumConstant; -import com.squareup.wire.schema.EnumType; -import com.squareup.wire.schema.Field; +import com.squareup.wire.Syntax; import com.squareup.wire.schema.Field.Label; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.OneOf; -import com.squareup.wire.schema.Options; -import com.squareup.wire.schema.ProtoFile; -import com.squareup.wire.schema.ProtoFile.Syntax; import com.squareup.wire.schema.ProtoType; -import com.squareup.wire.schema.Type; import com.squareup.wire.schema.internal.parser.OptionElement; import com.sun.xml.xsom.XSAttContainer; import com.sun.xml.xsom.XSAttGroupDecl; @@ -88,6 +80,15 @@ import com.sun.xml.xsom.parser.XSOMParser; import com.sun.xml.xsom.util.DomAnnotationParserFactory; +import no.entur.schema2proto.wire.MutableEnumConstant; +import no.entur.schema2proto.wire.MutableEnumType; +import no.entur.schema2proto.wire.MutableField; +import no.entur.schema2proto.wire.MutableMessageType; +import no.entur.schema2proto.wire.MutableOneOf; +import no.entur.schema2proto.wire.MutableOptions; +import no.entur.schema2proto.wire.MutableProtoFile; +import no.entur.schema2proto.wire.MutableType; + public class SchemaParser implements ErrorHandler { public static final String TYPE_SUFFIX = "Type"; @@ -97,9 +98,9 @@ public class SchemaParser implements ErrorHandler { private static final String DEFAULT_PROTO_PRIMITIVE = "string"; public static final String SIMPLECONTENT_VALUE_FIELD_NAME = "value"; - private final Map packageToProtoFileMap = new TreeMap<>(); + private final Map packageToProtoFileMap = new TreeMap<>(); - private final Map> elementDeclarationsPerMessageType = new HashMap<>(); + private final Map> elementDeclarationsPerMessageType = new HashMap<>(); private Set basicTypes; private int nestingLevel = 0; @@ -122,7 +123,7 @@ public SchemaParser(Schema2ProtoConfiguration configuration) { init(); } - public Map parse() throws SAXException, IOException { + public Map parse() throws SAXException, IOException { @SuppressWarnings("java:S2755") // Needs fix inside XSOM package SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); @@ -139,12 +140,12 @@ public Map parse() throws SAXException, IOException { return packageToProtoFileMap; } - private void addType(String namespace, Type type) { - ProtoFile file = getProtoFileForNamespace(namespace); + private void addType(String namespace, MutableType type) { + MutableProtoFile file = getProtoFileForNamespace(namespace); file.types().add(type); } - private ProtoFile getProtoFileForPackage(String packageName) { + private MutableProtoFile getProtoFileForPackage(String packageName) { if (StringUtils.trimToNull(packageName) == null) { packageName = Schema2ProtoConfiguration.DEFAULT_PROTO_PACKAGE; @@ -154,22 +155,22 @@ private ProtoFile getProtoFileForPackage(String packageName) { packageName = configuration.defaultProtoPackage; } - return packageToProtoFileMap.computeIfAbsent(packageName, k -> new ProtoFile(Syntax.PROTO_3, k)); + return packageToProtoFileMap.computeIfAbsent(packageName, k -> new MutableProtoFile(Syntax.PROTO_3, k)); } - private ProtoFile getProtoFileForNamespace(String namespace) { + private MutableProtoFile getProtoFileForNamespace(String namespace) { String packageName = NamespaceHelper.xmlNamespaceToProtoPackage(namespace, configuration.forceProtoPackage); return getProtoFileForPackage(packageName); } - private Type getType(String namespace, String typeName) { - ProtoFile protoFileForNamespace = getProtoFileForNamespace(namespace); - for (Type type : protoFileForNamespace.types()) { - if (type instanceof MessageType) { - if (((MessageType) type).getName().equals(typeName)) { + private MutableType getType(String namespace, String typeName) { + MutableProtoFile protoFileForNamespace = getProtoFileForNamespace(namespace); + for (MutableType type : protoFileForNamespace.types()) { + if (type instanceof MutableMessageType) { + if (((MutableMessageType) type).getName().equals(typeName)) { return type; } - } else if (type instanceof EnumType && ((EnumType) type).name().equals(typeName)) { + } else if (type instanceof MutableEnumType && ((MutableEnumType) type).name().equals(typeName)) { return type; } } @@ -208,7 +209,7 @@ private String processElement(XSElementDecl element, XSSchemaSet schemaSet) { if (element.getType() instanceof XSComplexType && element.getType() != schemaSet.getAnyType()) { cType = (XSComplexType) element.getType(); - MessageType t = processComplexType(cType, element.getName(), schemaSet, null, null); + MutableMessageType t = processComplexType(cType, element.getName(), schemaSet, null, null); if (cType.isGlobal()) { addType(element.getTargetNamespace(), t); } @@ -252,17 +253,17 @@ private String processSimpleType(XSSimpleType xs, String elementName) { return typeName; } - private void addField(MessageType message, Field newField) { + private void addField(MutableMessageType message, MutableField newField) { addField(message, null, newField); } - private void addField(MessageType message, OneOf oneOf, Field newField) { + private void addField(MutableMessageType message, MutableOneOf oneOf, MutableField newField) { // Verify with protolock that field number is not already used for a different field // Remove old fields of same type (element or attribute) - Field existingField = null; - for (Field f : message.fieldsAndOneOfFields()) { + MutableField existingField = null; + for (MutableField f : message.fieldsAndOneOfFields()) { if (newField.name().equals(f.name())) { existingField = f; break; @@ -286,23 +287,23 @@ private void addField(MessageType message, OneOf oneOf, Field newField) { } } - private MessageType createWrapper(String typeName, MessageType messageType, String wrapperFieldName, String targetNamespace, XSParticle particle, - String fieldDoc, Location location, XSComplexType parentType, String wrapperDoc) { + private MutableMessageType createWrapper(String typeName, MutableMessageType messageType, String wrapperFieldName, String targetNamespace, + XSParticle particle, String fieldDoc, Location location, XSComplexType parentType, String wrapperDoc) { // Add message type to file List messageOptions = new ArrayList<>(); - Options options = new Options(Options.MESSAGE_OPTIONS, messageOptions); + MutableOptions options = new MutableOptions(MutableOptions.MESSAGE_OPTIONS, messageOptions); - MessageType wrapperType = new MessageType(ProtoType.get(typeName), location, wrapperDoc, typeName, options); + MutableMessageType wrapperType = new MutableMessageType(ProtoType.get(typeName), location, wrapperDoc, typeName, options); wrapperType.setWrapperMessageType(true); messageType.nestedTypes().add(wrapperType); - Options fieldOptions = getFieldOptions(particle); + MutableOptions fieldOptions = getFieldOptions(particle); String fieldPackagename = NamespaceHelper.xmlNamespaceToProtoFieldPackagename(targetNamespace, configuration.forceProtoPackage); - Field field = new Field(fieldPackagename, location, Label.REPEATED, wrapperFieldName, fieldDoc, messageType.getNextFieldNum(), typeName, fieldOptions, - false); + MutableField field = new MutableField(fieldPackagename, location, Label.REPEATED, wrapperFieldName, fieldDoc, messageType.getNextFieldNum(), typeName, + fieldOptions, false); addField(messageType, field); @@ -313,13 +314,13 @@ private MessageType createWrapper(String typeName, MessageType messageType, Stri } - private void navigateSubTypes(XSParticle parentParticle, MessageType messageType, Set processedXmlObjects, XSSchemaSet schemaSet, + private void navigateSubTypes(XSParticle parentParticle, MutableMessageType messageType, Set processedXmlObjects, XSSchemaSet schemaSet, String enclosingName, String targetNamespace, XSComplexType enclosingType) { XSTerm currTerm = parentParticle.getTerm(); Label label = getLabel(parentParticle, currTerm); - Options fieldOptions = getFieldOptions(parentParticle); + MutableOptions fieldOptions = getFieldOptions(parentParticle); if (currTerm.isElementDecl()) { XSElementDecl currElementDecl = currTerm.asElementDecl(); @@ -336,13 +337,13 @@ private void navigateSubTypes(XSParticle parentParticle, MessageType messageType if (type.asSimpleType().isRestriction() && type.asSimpleType().getFacet(XSFacet.FACET_ENUMERATION) != null) { String enumName = createEnum(currElementDecl.getName(), type.asSimpleType().asRestriction(), type.isGlobal() ? null : messageType); - Field field = new Field(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, messageType.getNextFieldNum(), enumName, - fieldOptions, true); + MutableField field = new MutableField(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, + messageType.getNextFieldNum(), enumName, fieldOptions, true); addField(messageType, field); } else { String typeName = findFieldType(type); - Field field = new Field(basicTypes.contains(typeName) ? null : packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, - messageType.getNextFieldNum(), typeName, fieldOptions, true); + MutableField field = new MutableField(basicTypes.contains(typeName) ? null : packageName, fieldLocation, label, + currElementDecl.getName(), fieldDoc, messageType.getNextFieldNum(), typeName, fieldOptions, true); addField(messageType, field); } @@ -358,8 +359,8 @@ private void navigateSubTypes(XSParticle parentParticle, MessageType messageType findGlobalElementsBySubsumption(schemaSet, subsumptionSubstitutables, (XSComplexType) type); } if (substitutables.size() <= 1 && subsumptionSubstitutables.isEmpty()) { - Field field = new Field(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, messageType.getNextFieldNum(), - type.getName(), fieldOptions, true); + MutableField field = new MutableField(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, + messageType.getNextFieldNum(), type.getName(), fieldOptions, true); addField(messageType, field); } else { if (label == Label.REPEATED) { @@ -376,8 +377,8 @@ private void navigateSubTypes(XSParticle parentParticle, MessageType messageType oneOfName = currElementDecl.getName(); } - List fields = new ArrayList<>(); - OneOf oneOf = new OneOf(oneOfName, fieldDoc, fields, null); + List fields = new ArrayList<>(); + MutableOneOf oneOf = new MutableOneOf(oneOfName, fieldDoc, fields, null); messageType.oneOfs().add(oneOf); LinkedHashSet allSubtitutables = new LinkedHashSet<>(); @@ -396,9 +397,9 @@ private void navigateSubTypes(XSParticle parentParticle, MessageType messageType } else { // Local - MessageType referencedMessageType = processComplexType(type.asComplexType(), currElementDecl.getName(), schemaSet, null, null); - Field field = new Field(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, messageType.getNextFieldNum(), - referencedMessageType.getName(), fieldOptions, true); + MutableMessageType referencedMessageType = processComplexType(type.asComplexType(), currElementDecl.getName(), schemaSet, null, null); + MutableField field = new MutableField(packageName, fieldLocation, label, currElementDecl.getName(), fieldDoc, + messageType.getNextFieldNum(), referencedMessageType.getName(), fieldOptions, true); addField(messageType, field); if (!currElementDecl.isGlobal()) { @@ -418,7 +419,7 @@ private void navigateSubTypes(XSParticle parentParticle, MessageType messageType fieldLocation = messageType.location(); } - Field field = new Field(null, fieldLocation, label, "any", resolveDocumentationAnnotation(currTerm.asWildcard(), false), + MutableField field = new MutableField(null, fieldLocation, label, "any", resolveDocumentationAnnotation(currTerm.asWildcard(), false), messageType.getNextFieldNum(), "anyType", fieldOptions, true); addField(messageType, field); @@ -440,7 +441,7 @@ private void findGlobalElementsBySubsumption(XSSchemaSet schemaSet, Set optionElements = new ArrayList<>(ruleFactory.getValidationRule(parentParticle)); - return new Options(Options.FIELD_OPTIONS, optionElements); + return new MutableOptions(MutableOptions.FIELD_OPTIONS, optionElements); } @NotNull - private Options getFieldOptions(XSAttributeDecl attributeDecl) { + private MutableOptions getFieldOptions(XSAttributeDecl attributeDecl) { List optionElements = new ArrayList<>(); // First see if there are rules associated with attribute declaration @@ -473,13 +475,13 @@ private Options getFieldOptions(XSAttributeDecl attributeDecl) { List typeRule = ruleFactory.getValidationRule(attributeDecl.getType()); optionElements.addAll(typeRule); } - return new Options(Options.FIELD_OPTIONS, optionElements); + return new MutableOptions(MutableOptions.FIELD_OPTIONS, optionElements); } @NotNull - private Options getFieldOptions(XSSimpleType attributeDecl) { + private MutableOptions getFieldOptions(XSSimpleType attributeDecl) { List optionElements = new ArrayList<>(ruleFactory.getValidationRule(attributeDecl)); - return new Options(Options.FIELD_OPTIONS, optionElements); + return new MutableOptions(MutableOptions.FIELD_OPTIONS, optionElements); } public String findFieldType(XSType type) { @@ -557,7 +559,7 @@ private XSType getBaseType(XSSchemaSet schemaSet, XSType child) { } } - private MessageType processComplexType(XSComplexType complexType, String elementName, XSSchemaSet schemaSet, MessageType messageType, + private MutableMessageType processComplexType(XSComplexType complexType, String elementName, XSSchemaSet schemaSet, MutableMessageType messageType, Set processedXmlObjects) { nestingLevel++; @@ -590,7 +592,7 @@ private MessageType processComplexType(XSComplexType complexType, String element if (typeName == null) { typeName = elementName + GENERATED_NAME_PLACEHOLDER; } - messageType = (MessageType) getType(nameSpace, typeName); + messageType = (MutableMessageType) getType(nameSpace, typeName); if (messageType == null && !basicTypes.contains(typeName)) { @@ -611,15 +613,15 @@ private MessageType processComplexType(XSComplexType complexType, String element if (StringUtils.trimToNull(packageName) != null && !baseType.getTargetNamespace().equals(nameSpace)) { prefix = packageName + "."; } - OptionElement e = new OptionElement(XSD_MESSAGE_OPTIONS_PACKAGE + "." + MessageType.BASE_TYPE_MESSAGE_OPTION, OptionElement.Kind.STRING, - prefix + baseType.getName(), true); + OptionElement e = new OptionElement(XSD_MESSAGE_OPTIONS_PACKAGE + "." + MutableMessageType.BASE_TYPE_MESSAGE_OPTION, + OptionElement.Kind.STRING, prefix + baseType.getName(), true); messageOptions.add(e); } } - Options options = new Options(Options.MESSAGE_OPTIONS, messageOptions); + MutableOptions options = new MutableOptions(MutableOptions.MESSAGE_OPTIONS, messageOptions); // Add message type to file - messageType = new MessageType(ProtoType.get(typeName), location, doc, typeName, options); + messageType = new MutableMessageType(ProtoType.get(typeName), location, doc, typeName, options); if (complexType.isGlobal() || (complexType.getScope() != null && complexType.getScope().isGlobal())) { /* @@ -644,12 +646,12 @@ private MessageType processComplexType(XSComplexType complexType, String element if (configuration.inheritanceToComposition && complexType.getContentType().asParticle() != null) { - List parentTypes = new ArrayList<>(); + List parentTypes = new ArrayList<>(); while (parent != schemaSet.getAnyType() && parent.isComplexType()) { // Ensure no duplicate element parsing - MessageType parentType = processComplexType(parent.asComplexType(), elementName, schemaSet, null, null); + MutableMessageType parentType = processComplexType(parent.asComplexType(), elementName, schemaSet, null, null); processedXmlObjects.addAll(elementDeclarationsPerMessageType.get(parentType)); parentTypes.add(parentType); @@ -658,16 +660,16 @@ private MessageType processComplexType(XSComplexType complexType, String element if (!isAbstract(complexType)) { Collections.reverse(parentTypes); - for (MessageType parentMessageType : parentTypes) { + for (MutableMessageType parentMessageType : parentTypes) { String fieldDoc = parentMessageType.documentation(); List optionElements = new ArrayList<>(); - Options fieldOptions = new Options(Options.FIELD_OPTIONS, optionElements); + MutableOptions fieldOptions = new MutableOptions(MutableOptions.FIELD_OPTIONS, optionElements); int tag = messageType.getNextFieldNum(); Location fieldLocation = getLocation(complexType); - Field field = new Field(findPackageNameForType(parentMessageType), fieldLocation, null, "_" + parentMessageType.getName(), fieldDoc, tag, - parentMessageType.getName(), fieldOptions, true); + MutableField field = new MutableField(findPackageNameForType(parentMessageType), fieldLocation, null, "_" + parentMessageType.getName(), + fieldDoc, tag, parentMessageType.getName(), fieldOptions, true); addField(messageType, field); } @@ -719,7 +721,7 @@ private MessageType processComplexType(XSComplexType complexType, String element Location fieldLocation = getLocation(xsSimpleType); Label label = isList || isCurrentOrParentList(xsSimpleType) ? Label.REPEATED : null; - Options fieldOptions = getFieldOptions(xsSimpleType); + MutableOptions fieldOptions = getFieldOptions(xsSimpleType); String doc = resolveDocumentationAnnotation(complexType, false); if (name == null) { @@ -727,19 +729,19 @@ private MessageType processComplexType(XSComplexType complexType, String element String packageName = NamespaceHelper.xmlNamespaceToProtoFieldPackagename(xsSimpleType.getTargetNamespace(), configuration.forceProtoPackage); - Field field = new Field(basicTypes.contains(simpleTypeName) ? null : packageName, fieldLocation, label, SIMPLECONTENT_VALUE_FIELD_NAME, doc, - messageType.getNextFieldNum(), simpleTypeName, fieldOptions, true); + MutableField field = new MutableField(basicTypes.contains(simpleTypeName) ? null : packageName, fieldLocation, label, + SIMPLECONTENT_VALUE_FIELD_NAME, doc, messageType.getNextFieldNum(), simpleTypeName, fieldOptions, true); addField(messageType, field); } else if (basicTypes.contains(name)) { - Field field = new Field(null, fieldLocation, label, SIMPLECONTENT_VALUE_FIELD_NAME, doc, messageType.getNextFieldNum(), name, fieldOptions, - true); + MutableField field = new MutableField(null, fieldLocation, label, SIMPLECONTENT_VALUE_FIELD_NAME, doc, messageType.getNextFieldNum(), name, + fieldOptions, true); addField(messageType, field); } else { XSSimpleType primitiveType = xsSimpleType.getPrimitiveType(); if (primitiveType != null) { - Field field = new Field(null, fieldLocation, label, SIMPLECONTENT_VALUE_FIELD_NAME, doc, messageType.getNextFieldNum(), + MutableField field = new MutableField(null, fieldLocation, label, SIMPLECONTENT_VALUE_FIELD_NAME, doc, messageType.getNextFieldNum(), primitiveType.getName(), fieldOptions, true); addField(messageType, field); @@ -790,11 +792,11 @@ private boolean isAbstract(XSComplexType complexType) { } } - private String findPackageNameForType(MessageType parentMessageType) { + private String findPackageNameForType(MutableMessageType parentMessageType) { // Slow and dodgy - for (Map.Entry packageAndProtoFile : packageToProtoFileMap.entrySet()) { - ProtoFile file = packageAndProtoFile.getValue(); - for (Type t : file.types()) { + for (Map.Entry packageAndProtoFile : packageToProtoFileMap.entrySet()) { + MutableProtoFile file = packageAndProtoFile.getValue(); + for (MutableType t : file.types()) { if (t == parentMessageType) { return packageAndProtoFile.getKey(); } @@ -818,7 +820,7 @@ private Location getLocation(XSComponent t) { } - private void processAttributes(XSAttContainer xsAttContainer, MessageType messageType, Set processedXmlObjects) { + private void processAttributes(XSAttContainer xsAttContainer, MutableMessageType messageType, Set processedXmlObjects) { Iterator iterator = xsAttContainer.iterateDeclaredAttributeUses(); while (iterator.hasNext()) { XSAttributeUse attr = iterator.next(); @@ -833,7 +835,7 @@ private void processAttributes(XSAttContainer xsAttContainer, MessageType messag } - private void processAttribute(MessageType messageType, Set processedXmlObjects, XSAttributeUse attr) { + private void processAttribute(MutableMessageType messageType, Set processedXmlObjects, XSAttributeUse attr) { if (!processedXmlObjects.contains(attr)) { processedXmlObjects.add(attr); @@ -845,22 +847,22 @@ private void processAttribute(MessageType messageType, Set processedXmlO String doc = resolveDocumentationAnnotation(decl, false); int tag = messageType.getNextFieldNum(); Location fieldLocation = getLocation(decl); - Options fieldOptions = getFieldOptions(decl); + MutableOptions fieldOptions = getFieldOptions(decl); String packageName = NamespaceHelper.xmlNamespaceToProtoFieldPackagename(type.getTargetNamespace(), configuration.forceProtoPackage); Label label = type.isList() ? Label.REPEATED : null; if (type.isRestriction() && type.getFacet(XSFacet.FACET_ENUMERATION) != null) { String enumName = createEnum(fieldName, type.asRestriction(), decl.isLocal() ? messageType : null); - Field field = new Field(packageName, fieldLocation, label, fieldName, doc, tag, enumName, fieldOptions, false); + MutableField field = new MutableField(packageName, fieldLocation, label, fieldName, doc, tag, enumName, fieldOptions, false); field.setFromAttribute(true); addField(messageType, field); } else { String typeName = findFieldType(type); - Field field = new Field(basicTypes.contains(typeName) ? null : packageName, fieldLocation, label, fieldName, doc, tag, typeName, - fieldOptions, false); + MutableField field = new MutableField(basicTypes.contains(typeName) ? null : packageName, fieldLocation, label, fieldName, doc, tag, + typeName, fieldOptions, false); field.setFromAttribute(true); addField(messageType, field); @@ -882,7 +884,8 @@ private XSModelGroup getModelGroup(XSTerm term) { } } - private String createWrapperName(MessageType enclosingType, XSModelGroup.Compositor compositor, String enclosingName, XSComplexType enclosingComplexType) { + private String createWrapperName(MutableMessageType enclosingType, XSModelGroup.Compositor compositor, String enclosingName, + XSComplexType enclosingComplexType) { final String wrapperPrefix; if (XSModelGroup.SEQUENCE.equals(compositor)) { @@ -895,8 +898,8 @@ private String createWrapperName(MessageType enclosingType, XSModelGroup.Composi long numExistingWrappers = enclosingType.nestedTypes() .stream() - .filter(MessageType.class::isInstance) - .map(MessageType.class::cast) + .filter(MutableMessageType.class::isInstance) + .map(MutableMessageType.class::cast) .filter(e -> e.getName().startsWith(wrapperPrefix)) .count(); String wrapperPostfix = (enclosingComplexType.isGlobal() ? enclosingComplexType.getName() : enclosingName); @@ -908,8 +911,8 @@ private String createWrapperName(MessageType enclosingType, XSModelGroup.Composi } - private void processGroup(XSModelGroup modelGroup, XSParticle particle, MessageType messageType, Set processedXmlObjects, XSSchemaSet schemaSet, - String enclosingName, String targetNamespace, XSComplexType enclosingType) { + private void processGroup(XSModelGroup modelGroup, XSParticle particle, MutableMessageType messageType, Set processedXmlObjects, + XSSchemaSet schemaSet, String enclosingName, String targetNamespace, XSComplexType enclosingType) { XSModelGroup.Compositor compositor = modelGroup.getCompositor(); XSParticle[] children = modelGroup.getChildren(); @@ -945,8 +948,9 @@ private void processGroup(XSModelGroup modelGroup, XSParticle particle, MessageT } - private void createWrapperAndContinueProcessing(XSModelGroup modelGroup, XSParticle particle, MessageType messageType, Set processedXmlObjects, - XSSchemaSet schemaSet, XSParticle[] children, String typeName, String targetNamespace, String fieldName, XSComplexType enclosingType) { + private void createWrapperAndContinueProcessing(XSModelGroup modelGroup, XSParticle particle, MutableMessageType messageType, + Set processedXmlObjects, XSSchemaSet schemaSet, XSParticle[] children, String typeName, String targetNamespace, String fieldName, + XSComplexType enclosingType) { if (!processedXmlObjects.contains(particle)) { processedXmlObjects.add(particle); @@ -963,17 +967,18 @@ private void createWrapperAndContinueProcessing(XSModelGroup modelGroup, XSParti // Add message type to file List messageOptions = new ArrayList<>(); - Options options = new Options(Options.MESSAGE_OPTIONS, messageOptions); + MutableOptions options = new MutableOptions(MutableOptions.MESSAGE_OPTIONS, messageOptions); - MessageType wrapperType = new MessageType(ProtoType.get(typeName), location, doc, typeName, options); + MutableMessageType wrapperType = new MutableMessageType(ProtoType.get(typeName), location, doc, typeName, options); wrapperType.setWrapperMessageType(true); messageType.nestedTypes().add(wrapperType); - Options fieldOptions = getFieldOptions(particle); + MutableOptions fieldOptions = getFieldOptions(particle); String fieldPackagename = NamespaceHelper.xmlNamespaceToProtoFieldPackagename(targetNamespace, configuration.forceProtoPackage); - Field field = new Field(fieldPackagename, location, Label.REPEATED, fieldName, doc, messageType.getNextFieldNum(), typeName, fieldOptions, false); + MutableField field = new MutableField(fieldPackagename, location, Label.REPEATED, fieldName, doc, messageType.getNextFieldNum(), typeName, + fieldOptions, false); addField(messageType, field); @@ -984,7 +989,7 @@ private void createWrapperAndContinueProcessing(XSModelGroup modelGroup, XSParti } } - private void processGroupAsSequence(XSParticle particle, MessageType messageType, Set processedXmlObjects, XSSchemaSet schemaSet, + private void processGroupAsSequence(XSParticle particle, MutableMessageType messageType, Set processedXmlObjects, XSSchemaSet schemaSet, XSParticle[] children, String enclosingName, String targetNamespace, XSComplexType enclosingType) { for (XSParticle child : children) { XSTerm currTerm = child.getTerm(); @@ -1041,7 +1046,7 @@ private String resolveDocumentationAnnotation(XSComponent xsComponent, boolean k return StringUtils.trimToEmpty(b.toString()); } - private String createEnum(String elementName, XSRestrictionSimpleType type, MessageType enclosingType) { + private String createEnum(String elementName, XSRestrictionSimpleType type, MutableMessageType enclosingType) { Iterator it; String typeNameToUse; @@ -1061,14 +1066,14 @@ private String createEnum(String elementName, XSRestrictionSimpleType type, Mess } } - Type protoType = getType(type.getTargetNamespace(), typeNameToUse); + MutableType protoType = getType(type.getTargetNamespace(), typeNameToUse); if (protoType == null) { type = type.asRestriction(); Location location = getLocation(type); - List constants = new ArrayList<>(); + List constants = new ArrayList<>(); it = type.getDeclaredFacets().iterator(); int counter = 1; @@ -1081,12 +1086,13 @@ private String createEnum(String elementName, XSRestrictionSimpleType type, Mess if (!addedValues.contains(enumValue)) { addedValues.add(enumValue); - constants.add(new EnumConstant(location, enumValue, counter++, doc, new Options(Options.ENUM_VALUE_OPTIONS, optionElements))); + constants.add(new MutableEnumConstant(location, enumValue, counter++, doc, + new MutableOptions(MutableOptions.ENUM_VALUE_OPTIONS, optionElements))); } } List enumOptionElements = new ArrayList<>(); - Options enumOptions = new Options(Options.ENUM_OPTIONS, enumOptionElements); + MutableOptions enumOptions = new MutableOptions(MutableOptions.ENUM_OPTIONS, enumOptionElements); String doc = resolveDocumentationAnnotation(type, false); @@ -1097,13 +1103,13 @@ private String createEnum(String elementName, XSRestrictionSimpleType type, Mess definedProtoType = ProtoType.get(enclosingType.getName(), typeNameToUse); } - EnumType enumType = new EnumType(definedProtoType, location, doc, typeNameToUse, constants, new ArrayList<>(), enumOptions); + MutableEnumType enumType = new MutableEnumType(definedProtoType, location, doc, typeNameToUse, constants, new ArrayList<>(), enumOptions); if (enclosingType != null) { // if not already present boolean alreadyPresentAsNestedType = false; - for (Type t : enclosingType.nestedTypes()) { - if (t instanceof EnumType && ((EnumType) t).name().equals(typeNameToUse)) { + for (MutableType t : enclosingType.nestedTypes()) { + if (t instanceof MutableEnumType && ((MutableEnumType) t).name().equals(typeNameToUse)) { alreadyPresentAsNestedType = true; break; } diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/modifyproto/ModifyProto.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/modifyproto/ModifyProto.java index e3349d16..ed94c4c7 100644 --- a/schema2proto-lib/src/main/java/no/entur/schema2proto/modifyproto/ModifyProto.java +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/modifyproto/ModifyProto.java @@ -31,11 +31,14 @@ import java.io.InputStream; import java.io.Writer; import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -50,18 +53,12 @@ import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.EnumConstant; -import com.squareup.wire.schema.EnumType; import com.squareup.wire.schema.Field; -import com.squareup.wire.schema.IdentifierSet; import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.Options; import com.squareup.wire.schema.ProtoFile; +import com.squareup.wire.schema.PruningRules; import com.squareup.wire.schema.Reserved; import com.squareup.wire.schema.Schema; -import com.squareup.wire.schema.SchemaLoader; import com.squareup.wire.schema.Type; import com.squareup.wire.schema.internal.parser.OptionElement; import com.squareup.wire.schema.internal.parser.OptionReader; @@ -76,6 +73,15 @@ import no.entur.schema2proto.modifyproto.config.ModifyProtoConfiguration; import no.entur.schema2proto.modifyproto.config.NewEnumConstant; import no.entur.schema2proto.modifyproto.config.NewField; +import no.entur.schema2proto.wire.MutableEnumConstant; +import no.entur.schema2proto.wire.MutableEnumType; +import no.entur.schema2proto.wire.MutableField; +import no.entur.schema2proto.wire.MutableMessageType; +import no.entur.schema2proto.wire.MutableOptions; +import no.entur.schema2proto.wire.MutableProtoFile; +import no.entur.schema2proto.wire.MutableType; +import no.entur.schema2proto.wire.WireBuilders; +import no.entur.schema2proto.wire.WireSchemaLoader; public class ModifyProto { @@ -164,7 +170,6 @@ public static ModifyProtoConfiguration parseConfigurationFile(File configFile, F } public void modifyProto(ModifyProtoConfiguration configuration) throws IOException, InvalidProtobufException, InvalidConfigurationException { - SchemaLoader schemaLoader = new SchemaLoader(); // Collect source proto files (but not dependencies). Used to know which files should be written to .proto and which that should remain a dependency. Collection protoFiles = FileUtils.listFiles(configuration.inputDirectory, new String[] { "proto" }, true); @@ -172,60 +177,60 @@ public void modifyProto(ModifyProtoConfiguration configuration) throws IOExcepti .map(e -> configuration.inputDirectory.toURI().relativize(e.toURI()).getPath()) .collect(Collectors.toList()); + List sources = new ArrayList<>(); for (String importRootFolder : configuration.customImportLocations) { - schemaLoader.addSource(new File(configuration.basedir, importRootFolder).toPath()); + sources.add(new File(configuration.basedir, importRootFolder).toPath()); } + sources.add(configuration.inputDirectory.toPath()); - schemaLoader.addSource(configuration.inputDirectory); - - for (Path s : schemaLoader.sources()) { + for (java.nio.file.Path s : sources) { LOGGER.info("Linking proto from path {}", s); } - for (String s : schemaLoader.protos()) { - LOGGER.info("Linking proto {}", s); - } - Schema schema = schemaLoader.load(); + Schema schema = WireSchemaLoader.load(sources, Collections.emptyList()); // First run initial pruning, then look at the results and add referenced types from xsd.base_type - - IdentifierSet.Builder initialIdentifierSet = new IdentifierSet.Builder(); - initialIdentifierSet.exclude(configuration.excludes); - initialIdentifierSet.include(configuration.includes); - - IdentifierSet finalIterationIdentifiers; + Set excludes = new LinkedHashSet<>(configuration.excludes); + Set includes = new LinkedHashSet<>(configuration.includes); if (configuration.includeBaseTypes) { - finalIterationIdentifiers = followOneMoreLevel(initialIdentifierSet, schema); - } else { - finalIterationIdentifiers = initialIdentifierSet.build(); + includes = followOneMoreLevel(includes, excludes, schema); } - Schema prunedSchema = schema.prune(finalIterationIdentifiers); - for (String s : finalIterationIdentifiers.unusedExcludes()) { + + PruningRules finalIterationRules = buildPruningRules(includes, excludes); + Schema prunedSchema = schema.prune(finalIterationRules); + for (String s : finalIterationRules.unusedPrunes()) { LOGGER.warn("Unused exclude: {} (already excluded elsewhere or explicitly included?)", s); } - for (String s : finalIterationIdentifiers.unusedIncludes()) { + for (String s : finalIterationRules.unusedRoots()) { LOGGER.warn("Unused include: {} (already included elsewhere or explicitly excluded?) ", s); } + // Convert the pruned (immutable) schema into the mutable builder model used for editing and backwards-compat resolution + Map builderFilesByPath = new LinkedHashMap<>(); + for (ProtoFile pf : prunedSchema.getProtoFiles()) { + builderFilesByPath.put(pf.getLocation().getPath(), WireBuilders.fromProtoFile(pf)); + } + Collection builderFiles = builderFilesByPath.values(); + for (NewField newField : configuration.newFields) { - addField(newField, prunedSchema); + addField(newField, builderFiles); } for (ModifyField modifyField : configuration.modifyFields) { - modifyField(modifyField, prunedSchema); + modifyField(modifyField, builderFiles); } for (NewEnumConstant newEnumValue : configuration.newEnumConstants) { - addEnumConstant(newEnumValue, prunedSchema); + addEnumConstant(newEnumValue, builderFiles); } for (MergeFrom mergeFrom : configuration.mergeFrom) { - mergeFromFile(mergeFrom, prunedSchema, configuration); + mergeFromFile(mergeFrom, builderFiles, configuration); } for (FieldOption fieldOption : configuration.fieldOptions) { - addFieldOption(fieldOption, prunedSchema); + addFieldOption(fieldOption, builderFiles); } Set possibleIncompatibilitiesDetected = new HashSet<>(); @@ -234,26 +239,26 @@ public void modifyProto(ModifyProtoConfiguration configuration) throws IOExcepti try { ProtolockBackwardsCompatibilityChecker backwardsCompatibilityChecker = new ProtolockBackwardsCompatibilityChecker(); backwardsCompatibilityChecker.init(configuration.protoLockFile); - ImmutableList files = prunedSchema.protoFiles(); - - files.stream().forEach(file -> possibleIncompatibilitiesDetected.add(backwardsCompatibilityChecker.resolveBackwardIncompatibilities(file))); + for (MutableProtoFile file : builderFiles) { + possibleIncompatibilitiesDetected.add(backwardsCompatibilityChecker.resolveBackwardIncompatibilities(file)); + } } catch (FileNotFoundException e) { throw new InvalidConfigurationException("Could not find proto.lock file, check configuration"); } } if (configuration.includeGoPackageOptions) { - includeGoPackageNameOptions(prunedSchema.protoFiles(), configuration.goPackageSourcePrefix); + includeGoPackageNameOptions(builderFiles, configuration.goPackageSourcePrefix); } Set emptyImportLocations = protosLoaded.stream() - .map(prunedSchema::protoFile) + .map(builderFilesByPath::get) .filter(Objects::nonNull) .filter(this::isEmptyFile) .map(p -> p.location().getPath()) .collect(Collectors.toSet()); - protosLoaded.stream().map(prunedSchema::protoFile).filter(Objects::nonNull).filter(p -> !isEmptyFile(p)).forEach(file -> { + protosLoaded.stream().map(builderFilesByPath::get).filter(Objects::nonNull).filter(p -> !isEmptyFile(p)).forEach(file -> { file.imports().removeIf(emptyImportLocations::contains); file.publicImports().removeIf(emptyImportLocations::contains); File outputFile = new File(configuration.outputDirectory, file.location().getPath()); @@ -274,10 +279,10 @@ public void modifyProto(ModifyProtoConfiguration configuration) throws IOExcepti } - private void includeGoPackageNameOptions(Collection protoFiles, String goPackageSourcePrefix) { - for (ProtoFile protoFile : protoFiles) { + private void includeGoPackageNameOptions(Collection protoFiles, String goPackageSourcePrefix) { + for (MutableProtoFile protoFile : protoFiles) { String optionName = "go_package"; - boolean alreadySet = protoFile.options().getOptionElements().stream().anyMatch(existingOption -> optionName.equals(existingOption.getName())); + boolean alreadySet = protoFile.options().optionElements().stream().anyMatch(existingOption -> optionName.equals(existingOption.getName())); if (!alreadySet) { String goPackageName = packageNameToGoPackageName(goPackageSourcePrefix, protoFile.packageName()); OptionElement optionElement = new OptionElement(optionName, OptionElement.Kind.STRING, goPackageName, false); @@ -286,109 +291,113 @@ private void includeGoPackageNameOptions(Collection protoFiles, Strin } } - private boolean isEmptyFile(ProtoFile p) { + private boolean isEmptyFile(MutableProtoFile p) { return p.types().isEmpty() && p.getExtendList().isEmpty(); } - private IdentifierSet followOneMoreLevel(IdentifierSet.Builder identifierSetBuilder, Schema schema) { + /** + * Builds the pruning rules, mirroring the vendored {@code IdentifierSet} semantics where excludes take precedence over includes: an identifier present in + * both is pruned, not rooted. Stock wire's {@link PruningRules} rejects the same identifier in both roots and prunes, so the overlap is dropped from the + * roots here. + */ + private PruningRules buildPruningRules(Set includes, Set excludes) { + PruningRules.Builder builder = new PruningRules.Builder(); + builder.prune(excludes); + builder.addRoot(includes.stream().filter(i -> !excludes.contains(i)).collect(Collectors.toList())); + return builder.build(); + } - // Prune schema using current schema - IdentifierSet identifierSet = identifierSetBuilder.build(); - Schema prunedSchema = schema.prune(identifierSet); + private Set followOneMoreLevel(Set includes, Set excludes, Schema schema) { - // Add new base types to follow - IdentifierSet.Builder updatedIdentifierSetBuilder = new IdentifierSet.Builder(); - updatedIdentifierSetBuilder.exclude(identifierSet.excludes()); - updatedIdentifierSetBuilder.include(identifierSet.includes()); + // Prune schema using current rules + Schema prunedSchema = schema.prune(buildPruningRules(includes, excludes)); - for (ProtoFile file : prunedSchema.protoFiles()) { - for (Type t : file.types()) { - includeBaseType(updatedIdentifierSetBuilder, t, file.packageName()); + // Add new base types to follow + Set updatedIncludes = new LinkedHashSet<>(includes); + for (ProtoFile file : prunedSchema.getProtoFiles()) { + for (Type t : file.getTypes()) { + includeBaseType(updatedIncludes, t, file.getPackageName()); } } - IdentifierSet updatedIdentifierSet = updatedIdentifierSetBuilder.build(); - // More dependencies found, iterate once more - if (!identifierSet.includes().equals(updatedIdentifierSet.includes())) { - return followOneMoreLevel(updatedIdentifierSetBuilder, schema); + if (!includes.equals(updatedIncludes)) { + return followOneMoreLevel(updatedIncludes, excludes, schema); } else { // Another iteration yielded no more identifiers to include, we're done - return updatedIdentifierSet; + return updatedIncludes; } } - private void includeBaseType(IdentifierSet.Builder b, Type type, String enclosingPackage) { - if (type.options() != null) { - List baseTypeInherits = type.options() - .getOptionElements() + private void includeBaseType(Set includes, Type type, String enclosingPackage) { + if (type.getOptions() != null) { + List baseTypeInherits = type.getOptions() + .getElements() .stream() - .filter(e -> e.getName().equals(MessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME)) + .filter(e -> e.getName().equals(MutableMessageType.XSD_BASE_TYPE_MESSAGE_OPTION_NAME)) .collect(Collectors.toList()); baseTypeInherits.stream().forEach(e -> { String baseTypeValue = (String) e.getValue(); if (baseTypeValue.contains(".")) { - b.include(baseTypeValue); + includes.add(baseTypeValue); } else { // No package in includeBaseType statement String fullType = baseTypeValue; if (enclosingPackage != null) { fullType = enclosingPackage + "." + fullType; } - b.include(fullType); + includes.add(fullType); } }); } - type.nestedTypes().stream().forEach(e -> includeBaseType(b, e, enclosingPackage)); + type.getNestedTypes().stream().forEach(e -> includeBaseType(includes, e, enclosingPackage)); } - private void mergeFromFile(MergeFrom mergeFrom, Schema prunedSchema, ModifyProtoConfiguration configuration) throws IOException { + private void mergeFromFile(MergeFrom mergeFrom, Collection builderFiles, ModifyProtoConfiguration configuration) throws IOException { - SchemaLoader schemaLoader = new SchemaLoader(); + List sources = new ArrayList<>(); for (String importRootFolder : configuration.customImportLocations) { - schemaLoader.addSource(new File(configuration.basedir, importRootFolder).toPath()); + sources.add(new File(configuration.basedir, importRootFolder).toPath()); } if (mergeFrom.sourceFolder.isAbsolute()) { - schemaLoader.addSource(mergeFrom.sourceFolder); + sources.add(mergeFrom.sourceFolder.toPath()); } else { - schemaLoader.addSource(new File(configuration.basedir, mergeFrom.sourceFolder.getPath())); + sources.add(new File(configuration.basedir, mergeFrom.sourceFolder.getPath()).toPath()); } if (configuration.inputDirectory.isAbsolute()) { - schemaLoader.addSource(configuration.inputDirectory); + sources.add(configuration.inputDirectory.toPath()); } else { - schemaLoader.addSource(new File(configuration.basedir, configuration.inputDirectory.getPath())); + sources.add(new File(configuration.basedir, configuration.inputDirectory.getPath()).toPath()); } - schemaLoader.addProto(mergeFrom.protoFile); - - Schema schema = schemaLoader.load(); + Schema schema = WireSchemaLoader.load(sources, Collections.singletonList(mergeFrom.protoFile)); ProtoFile source = schema.protoFile(mergeFrom.protoFile); - ProtoFile destination = prunedSchema.protoFileForPackage(source.packageName()); + MutableProtoFile sourceBuilder = WireBuilders.fromProtoFile(source); + MutableProtoFile destination = findProtoFileForPackage(builderFiles, sourceBuilder.packageName()); if (destination == null) { throw new IllegalArgumentException("Destination protofile not found"); } else { - destination.mergeFrom(source); + destination.mergeFrom(sourceBuilder); } } - private void addEnumConstant(NewEnumConstant newEnumConstant, Schema prunedSchema) throws InvalidProtobufException { - Type targetEnumType = prunedSchema.getType(newEnumConstant.targetEnumType); - if (targetEnumType instanceof EnumType) { - List optionElements = new ArrayList<>(); - Options options = new Options(Options.ENUM_VALUE_OPTIONS, optionElements); + private void addEnumConstant(NewEnumConstant newEnumConstant, Collection builderFiles) throws InvalidProtobufException { + MutableEnumType enumType = findEnumType(builderFiles, newEnumConstant.targetEnumType); + if (enumType != null) { + MutableOptions options = new MutableOptions(MutableOptions.ENUM_VALUE_OPTIONS, new ArrayList<>()); Location location = new Location("", "", -1, -1); - EnumConstant enumConstant = new EnumConstant(location, newEnumConstant.name, newEnumConstant.fieldNumber, newEnumConstant.documentation, options); - EnumType enumType = (EnumType) targetEnumType; + MutableEnumConstant enumConstant = new MutableEnumConstant(location, newEnumConstant.name, newEnumConstant.fieldNumber, + newEnumConstant.documentation, options); // Duplicate check - Optional existing = enumType.constants() + Optional existing = enumType.constants() .stream() .filter(e -> e.getName().equals(enumConstant.getName()) || e.getTag() == enumConstant.getTag()) .findFirst(); @@ -402,9 +411,9 @@ private void addEnumConstant(NewEnumConstant newEnumConstant, Schema prunedSchem } } - private void addField(NewField newField, Schema prunedSchema) throws InvalidProtobufException { + private void addField(NewField newField, Collection builderFiles) throws InvalidProtobufException { - MessageType type = (MessageType) prunedSchema.getType(newField.targetMessageType); + MutableMessageType type = findMessageType(builderFiles, newField.targetMessageType); if (type == null) { throw new InvalidProtobufException("Did not find existing type " + newField.targetMessageType); } else { @@ -435,8 +444,7 @@ private void addField(NewField newField, Schema prunedSchema) throws InvalidProt reservedFields.addAll(updatedReservedFields); } - List optionElements = new ArrayList<>(); - Options options = new Options(Options.FIELD_OPTIONS, optionElements); + MutableOptions options = new MutableOptions(MutableOptions.FIELD_OPTIONS, new ArrayList<>()); int tag = newField.fieldNumber; String fieldPackage = StringUtils.substringBeforeLast(newField.type, "."); @@ -452,9 +460,9 @@ private void addField(NewField newField, Schema prunedSchema) throws InvalidProt } Location location = new Location("", "", -1, -1); - Field field = new Field(fieldPackage, location, label, newField.name, StringUtils.trimToEmpty(newField.documentation), tag, null, newField.type, - options, false, false); - List updatedFields = new ArrayList<>(type.fields()); + MutableField field = new MutableField(fieldPackage, location, label, newField.name, StringUtils.trimToEmpty(newField.documentation), tag, null, + newField.type, options, false, false); + List updatedFields = new ArrayList<>(type.fields()); updatedFields.add(field); type.setDeclaredFields(updatedFields); @@ -462,10 +470,10 @@ private void addField(NewField newField, Schema prunedSchema) throws InvalidProt if (importStatement != null) { String targetPackageName = StringUtils.trimToNull(StringUtils.substringBeforeLast(newField.targetMessageType, ".")); - ProtoFile targetFile = prunedSchema.protoFileForPackage(targetPackageName); + MutableProtoFile targetFile = findProtoFileForPackage(builderFiles, targetPackageName); if (newField.targetMessageType.equals(targetPackageName)) { // no package name on target - targetFile = prunedSchema.protoFileForPackage(null); + targetFile = findProtoFileForPackage(builderFiles, null); } targetFile.imports().add(importStatement); } @@ -474,12 +482,12 @@ private void addField(NewField newField, Schema prunedSchema) throws InvalidProt } - private void modifyField(ModifyField modifyField, Schema prunedSchema) throws InvalidProtobufException { - MessageType type = (MessageType) prunedSchema.getType(modifyField.targetMessageType); + private void modifyField(ModifyField modifyField, Collection builderFiles) throws InvalidProtobufException { + MutableMessageType type = findMessageType(builderFiles, modifyField.targetMessageType); if (type == null) { throw new InvalidProtobufException("Did not find existing type " + modifyField.targetMessageType); } - Field field = type.field(modifyField.field); + MutableField field = type.field(modifyField.field); if (field == null) { throw new InvalidProtobufException("Did not find existing field " + modifyField.field); } @@ -492,21 +500,79 @@ private void modifyField(ModifyField modifyField, Schema prunedSchema) throws In } } - public void addFieldOption(FieldOption fieldOption, Schema prunedSchema) throws InvalidProtobufException { - MessageType type = (MessageType) prunedSchema.getType(fieldOption.targetMessageType); + public void addFieldOption(FieldOption fieldOption, Collection builderFiles) throws InvalidProtobufException { + MutableMessageType type = findMessageType(builderFiles, fieldOption.targetMessageType); if (type == null) { throw new InvalidProtobufException("Did not find existing type " + fieldOption.targetMessageType); } - Field field = type.field(fieldOption.field); + MutableField field = type.field(fieldOption.field); if (field == null) { throw new InvalidProtobufException("Did not find existing field " + fieldOption.field); } if (StringUtils.isEmpty(fieldOption.option)) { throw new InvalidProtobufException("Missing option for field " + fieldOption.field); } - OptionReader reader = new OptionReader(new SyntaxReader(fieldOption.option.toCharArray(), null)); + OptionReader reader = new OptionReader(new SyntaxReader(fieldOption.option.toCharArray(), Location.get("", ""))); reader.readOptions().forEach(option -> field.options().add(option)); + // A field that uses the buf.validate extension must import its definition for the result to be valid protobuf + if (fieldOption.option.contains("buf.validate")) { + MutableProtoFile file = findProtoFileForType(builderFiles, fieldOption.targetMessageType); + if (file != null && !file.imports().contains("buf/validate/validate.proto")) { + file.imports().add("buf/validate/validate.proto"); + } + } + } + + private MutableProtoFile findProtoFileForType(Collection builderFiles, String qualifiedName) { + for (MutableProtoFile file : builderFiles) { + if (findType(file.types(), qualifiedName) != null) { + return file; + } + } + return null; + } + + private MutableMessageType findMessageType(Collection builderFiles, String qualifiedName) { + for (MutableProtoFile file : builderFiles) { + MutableType type = findType(file.types(), qualifiedName); + if (type instanceof MutableMessageType) { + return (MutableMessageType) type; + } + } + return null; + } + + private MutableEnumType findEnumType(Collection builderFiles, String qualifiedName) { + for (MutableProtoFile file : builderFiles) { + MutableType type = findType(file.types(), qualifiedName); + if (type instanceof MutableEnumType) { + return (MutableEnumType) type; + } + } + return null; + } + + private MutableType findType(List types, String qualifiedName) { + for (MutableType type : types) { + if (type.type().toString().equals(qualifiedName)) { + return type; + } + MutableType nested = findType(type.nestedTypes(), qualifiedName); + if (nested != null) { + return nested; + } + } + return null; + } + + private MutableProtoFile findProtoFileForPackage(Collection builderFiles, String packageName) { + for (MutableProtoFile file : builderFiles) { + if (Objects.equals(file.packageName(), packageName)) { + return file; + } + } + return null; } } diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumConstant.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumConstant.java new file mode 100644 index 00000000..fc7f71c1 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumConstant.java @@ -0,0 +1,76 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import com.squareup.wire.schema.EnumConstant; +import com.squareup.wire.schema.Location; + +/** Mutable builder analogue of {@link com.squareup.wire.schema.EnumConstant}. */ +public class MutableEnumConstant { + + private final Location location; + private String name; + private int tag; + private final String documentation; + private final MutableOptions options; + + public MutableEnumConstant(Location location, String name, int tag, String documentation, MutableOptions options) { + this.location = location; + this.name = name; + this.tag = tag; + this.documentation = documentation; + this.options = options; + } + + public Location getLocation() { + return location; + } + + public String getName() { + return name; + } + + public void updateName(String name) { + this.name = name; + } + + public int getTag() { + return tag; + } + + public void updateTag(int newTag) { + this.tag = newTag; + } + + public String getDocumentation() { + return documentation; + } + + public MutableOptions options() { + return options; + } + + public EnumConstant toWire() { + return new EnumConstant(location, name, tag, documentation == null ? "" : documentation, options.toWire()); + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumType.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumType.java new file mode 100644 index 00000000..3937e200 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableEnumType.java @@ -0,0 +1,135 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.EnumType; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.Reserved; +import com.squareup.wire.schema.Type; + +/** Mutable builder analogue of {@link com.squareup.wire.schema.EnumType}. */ +public class MutableEnumType extends MutableType { + + private ProtoType protoType; + private final Location location; + private String documentation; + private String name; + private final List constants; + private final List reserveds; + private final MutableOptions options; + + public MutableEnumType(ProtoType protoType, Location location, String documentation, String name, List constants, + List reserveds, MutableOptions options) { + this.protoType = protoType; + this.location = location; + this.documentation = documentation; + this.name = name; + this.constants = constants != null ? constants : new ArrayList<>(); + this.reserveds = reserveds != null ? reserveds : new ArrayList<>(); + this.options = options; + } + + public String name() { + return name; + } + + public void updateName(String newName) { + this.name = newName; + protoType = ProtoType.get(protoType.getEnclosingTypeOrPackage(), newName); + } + + public List constants() { + return constants; + } + + public List getReserveds() { + return reserveds; + } + + public List reserveds() { + return reserveds; + } + + public void addReserved(String documentation, Location location, int tag) { + boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesTag(tag)); + if (!alreadyReserved) { + reserveds.add(new Reserved(location, documentation == null ? "" : documentation, List.of(tag))); + } + } + + public void addReserved(String documentation, Location location, String constantName) { + boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesName(constantName)); + if (!alreadyReserved) { + reserveds.add(new Reserved(location, documentation == null ? "" : documentation, Collections.singletonList(constantName))); + } + } + + @Override + public ProtoType type() { + return protoType; + } + + @Override + public Location location() { + return location; + } + + @Override + public String documentation() { + return documentation; + } + + @Override + public void updateDocumentation(String documentation) { + this.documentation = documentation; + } + + @Override + public MutableOptions options() { + return options; + } + + @Override + public List nestedTypes() { + return Collections.emptyList(); + } + + @Override + public Type toWire(Syntax syntax) { + List wireConstants = constants.stream().map(MutableEnumConstant::toWire).collect(Collectors.toList()); + return new EnumType(protoType, location, documentation == null ? "" : documentation, name, wireConstants, new ArrayList<>(reserveds), options.toWire(), + syntax); + } + + @Override + public String toString() { + return "EnumType [name=" + name + "]"; + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableField.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableField.java new file mode 100644 index 00000000..cd0de2e4 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableField.java @@ -0,0 +1,177 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.Collections; +import java.util.List; + +import com.squareup.wire.schema.Field; +import com.squareup.wire.schema.Field.Label; +import com.squareup.wire.schema.Location; + +/** + * Mutable builder analogue of {@link com.squareup.wire.schema.Field}. + * + *

+ * {@code packageName} is schema2proto's transient notion of the proto package of the field's referenced type (used to compute imports); it is folded into + * {@code elementType} before serialization. The stock wire {@code Field} carries no such concept (its {@code namespaces} are the enclosing scope, irrelevant + * for canonical serialization), so {@link #toWire()} emits an empty namespaces list. + */ +public class MutableField { + + private String packageName; + private final Location location; + private Label label; + private String name; + private String documentation; + private int tag; + private final String defaultValue; + private String elementType; + private final MutableOptions options; + private final boolean extension; + private boolean isOneOf; + + private boolean fromElement; + private boolean fromAttribute; + + public MutableField(String packageName, Location location, Label label, String name, String documentation, int tag, String elementType, + MutableOptions options, boolean extension) { + this(packageName, location, label, name, documentation, tag, null, elementType, options, extension, false); + } + + public MutableField(String packageName, Location location, Label label, String name, String documentation, int tag, String defaultValue, String elementType, + MutableOptions options, boolean extension, boolean fromElement) { + this.packageName = packageName; + this.location = location; + this.label = label; + this.name = name; + this.documentation = documentation; + this.tag = tag; + this.defaultValue = defaultValue; + this.elementType = elementType; + this.options = options; + this.extension = extension; + this.fromElement = fromElement; + } + + public String name() { + return name; + } + + public void updateName(String newFieldName) { + this.name = newFieldName; + } + + public String getElementType() { + return elementType; + } + + public void updateElementType(String newFieldType) { + this.elementType = newFieldType; + } + + public int tag() { + return tag; + } + + public void updateTag(int updatedTag) { + this.tag = updatedTag; + } + + public Label label() { + return label; + } + + public void setLabel(Label label) { + this.label = label; + } + + public String documentation() { + return documentation; + } + + public void updateDocumentation(String newDocumentation) { + this.documentation = newDocumentation; + } + + public String packageName() { + return packageName; + } + + public void updatePackageName(String newPackageName) { + this.packageName = newPackageName; + } + + public void clearPackageName() { + this.packageName = null; + } + + public MutableOptions options() { + return options; + } + + public Location location() { + return location; + } + + public boolean isExtension() { + return extension; + } + + public boolean isFromAttribute() { + return fromAttribute; + } + + public void setFromAttribute(boolean fromAttribute) { + this.fromAttribute = fromAttribute; + } + + public boolean isFromElement() { + return fromElement; + } + + public void setFromElement(boolean fromElement) { + this.fromElement = fromElement; + } + + void setOneOf(boolean isOneOf) { + this.isOneOf = isOneOf; + } + + /** + * @param order the field's position within its enclosing message. Stock wire serializes message fields sorted by their {@link Location} (line, column) + * rather than by list order, so we encode the intended emission order as the location line to reproduce the vendored serializer's list-order + * output. + */ + public Field toWire(int order) { + List namespaces = Collections.emptyList(); + Location orderedLocation = new Location("", "", order, 0); + return new Field(namespaces, orderedLocation, label, name, documentation == null ? "" : documentation, tag, defaultValue, elementType, options.toWire(), + extension, isOneOf, null); + } + + @Override + public String toString() { + return "Field{name=" + name + ", tag=" + tag + ", type=" + elementType + "}"; + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableMessageType.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableMessageType.java new file mode 100644 index 00000000..25fc7f02 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableMessageType.java @@ -0,0 +1,224 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.MessageType; +import com.squareup.wire.schema.Options; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.Reserved; +import com.squareup.wire.schema.Type; + +/** Mutable builder analogue of {@link com.squareup.wire.schema.MessageType}. */ +public class MutableMessageType extends MutableType { + + // schema2proto-specific constants, formerly bolted onto the vendored wire MessageType. + public static final String XSD_MESSAGE_OPTIONS_PACKAGE = "xsd"; + public static final String BASE_TYPE_MESSAGE_OPTION = "base_type"; + public static final String XSD_BASE_TYPE_MESSAGE_OPTION_NAME = XSD_MESSAGE_OPTIONS_PACKAGE + "." + BASE_TYPE_MESSAGE_OPTION; + public static final int FIELD_NUM_INCREMENT = 10; + + private ProtoType protoType; + private final Location location; + private String documentation; + private String name; + private List declaredFields; + private final List oneOfs; + private final List nestedTypes; + private final List reserveds; + private final MutableOptions options; + + private int fieldNum = 0; + private boolean wrapperMessageType = false; + + public MutableMessageType(ProtoType protoType, Location location, String documentation, String name, MutableOptions options) { + this.protoType = protoType; + this.location = location; + this.documentation = documentation; + this.name = name; + this.options = options; + this.declaredFields = new ArrayList<>(); + this.oneOfs = new ArrayList<>(); + this.nestedTypes = new ArrayList<>(); + this.reserveds = new ArrayList<>(); + } + + public boolean isWrapperMessageType() { + return wrapperMessageType; + } + + public void setWrapperMessageType(boolean wrapperMessageType) { + this.wrapperMessageType = wrapperMessageType; + } + + public List getReserveds() { + return reserveds; + } + + public void addReserved(String documentation, Location location, int tag) { + boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesTag(tag)); + if (!alreadyReserved) { + reserveds.add(new Reserved(location, documentation == null ? "" : documentation, List.of(tag))); + } + } + + public void addReserved(String documentation, Location location, String fieldName) { + boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesName(fieldName)); + if (!alreadyReserved) { + reserveds.add(new Reserved(location, documentation == null ? "" : documentation, List.of(fieldName))); + } + } + + public int getNextFieldNum() { + fieldNum++; + return fieldNum; + } + + public void advanceFieldNum() { + if (fieldNum == 0) { + return; + } + int newFieldNum = (fieldNum + FIELD_NUM_INCREMENT) - (fieldNum % FIELD_NUM_INCREMENT); + if (newFieldNum - (FIELD_NUM_INCREMENT / 3) < fieldNum) { + fieldNum = newFieldNum; + advanceFieldNum(); + } else { + fieldNum = newFieldNum; + } + } + + public String getName() { + return name; + } + + public void updateName(String newName) { + this.name = newName; + protoType = ProtoType.get(protoType.getEnclosingTypeOrPackage(), newName); + } + + @Override + public ProtoType type() { + return protoType; + } + + @Override + public Location location() { + return location; + } + + @Override + public String documentation() { + return documentation; + } + + @Override + public void updateDocumentation(String documentation) { + this.documentation = documentation; + } + + @Override + public MutableOptions options() { + return options; + } + + @Override + public List nestedTypes() { + return nestedTypes; + } + + public void addField(MutableField f) { + declaredFields.add(f); + } + + public void setDeclaredFields(List newFields) { + this.declaredFields = newFields; + } + + /** Mirrors the vendored behaviour: returns a fresh combined list (so sorting it does not reorder declared fields). */ + public List fields() { + return new ArrayList<>(declaredFields); + } + + public void removeDeclaredField(MutableField f) { + declaredFields.remove(f); + } + + public List fieldsAndOneOfFields() { + List result = new ArrayList<>(declaredFields); + for (MutableOneOf oneOf : oneOfs) { + result.addAll(oneOf.fields()); + } + return result; + } + + public MutableField field(String name) { + for (MutableField field : declaredFields) { + if (field.name().equals(name)) { + return field; + } + } + for (MutableOneOf oneOf : oneOfs) { + for (MutableField field : oneOf.fields()) { + if (field.name().equals(name)) { + return field; + } + } + } + return null; + } + + public List oneOfs() { + return oneOfs; + } + + public void removeOneOf(MutableOneOf oneOfToRemove) { + oneOfs.remove(oneOfToRemove); + } + + @Override + public Type toWire(Syntax syntax) { + // Stock wire serializes message fields sorted by Location (line, column), not by list order. Encode each field's list position + // as its location so the vendored serializer's list-order output (declared fields first, then oneOfs) is reproduced exactly. + java.util.concurrent.atomic.AtomicInteger order = new java.util.concurrent.atomic.AtomicInteger(0); + List wireDeclaredFields = declaredFields.stream() + .map(f -> f.toWire(order.getAndIncrement())) + .collect(Collectors.toList()); + List wireOneOfs = oneOfs.stream().map(o -> o.toWire(order)).collect(Collectors.toList()); + List wireNestedTypes = nestedTypes.stream().map(t -> t.toWire(syntax)).collect(Collectors.toList()); + Options wireOptions = options.toWire(); + return new MessageType(protoType, location, documentation == null ? "" : documentation, name, wireDeclaredFields, + new ArrayList<>() /* extensionFields */, wireOneOfs, wireNestedTypes, Collections.emptyList() /* nestedExtendList */, + Collections.emptyList() /* extensionsList */, new ArrayList<>(reserveds), wireOptions, syntax); + } + + @Override + public String toString() { + return "MessageType [name=" + name + "]"; + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOneOf.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOneOf.java new file mode 100644 index 00000000..584c270a --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOneOf.java @@ -0,0 +1,80 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.List; + +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.OneOf; +import com.squareup.wire.schema.Options; + +/** Mutable builder analogue of {@link com.squareup.wire.schema.OneOf}. */ +public class MutableOneOf { + + private final String name; + private String documentation; + private final List fields; + private final MutableOptions options; + + public MutableOneOf(String name, String documentation, List fields, MutableOptions options) { + this.name = name; + this.documentation = documentation; + this.fields = fields != null ? fields : new ArrayList<>(); + this.options = options != null ? options : new MutableOptions(MutableOptions.ONEOF_OPTIONS, new ArrayList<>()); + } + + public String name() { + return name; + } + + public String documentation() { + return documentation; + } + + public void updateDocumentation(String documentation) { + this.documentation = documentation; + } + + public List fields() { + return fields; + } + + public void addField(MutableField newField) { + newField.setOneOf(true); + fields.add(newField); + } + + public OneOf toWire(java.util.concurrent.atomic.AtomicInteger order) { + List wireFields = new ArrayList<>(); + for (MutableField f : fields) { + f.setOneOf(true); + wireFields.add(f.toWire(order.getAndIncrement())); + } + return new OneOf(name, documentation == null ? "" : documentation, wireFields, Location.get("", ""), options.toWire()); + } + + Options optionsToWire() { + return options.toWire(); + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOptions.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOptions.java new file mode 100644 index 00000000..de080172 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableOptions.java @@ -0,0 +1,63 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.List; + +import com.squareup.wire.schema.Options; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.internal.parser.OptionElement; + +/** + * Mutable builder analogue of {@link Options}. schema2proto manipulates option elements in place during conversion; the stock wire {@code Options} is + * immutable, so we accumulate elements here and emit an immutable {@code Options} via {@link #toWire()} at serialization time. + */ +public record MutableOptions(ProtoType optionType, List optionElements) { + + // Re-export the option-type constants so consumers keep referencing them through Options.* (see stock companion @JvmField). + public static final ProtoType FILE_OPTIONS = Options.FILE_OPTIONS; + public static final ProtoType MESSAGE_OPTIONS = Options.MESSAGE_OPTIONS; + public static final ProtoType FIELD_OPTIONS = Options.FIELD_OPTIONS; + public static final ProtoType ONEOF_OPTIONS = Options.ONEOF_OPTIONS; + public static final ProtoType ENUM_OPTIONS = Options.ENUM_OPTIONS; + public static final ProtoType ENUM_VALUE_OPTIONS = Options.ENUM_VALUE_OPTIONS; + + public MutableOptions(ProtoType optionType, List optionElements) { + this.optionType = optionType; + this.optionElements = new ArrayList<>(optionElements); + } + + public void add(OptionElement option) { + optionElements.add(option); + } + + public void replaceOption(String optionName, OptionElement element) { + optionElements.removeIf(e -> e.getName().equals(optionName)); + optionElements.add(element); + } + + public Options toWire() { + return new Options(optionType, new ArrayList<>(optionElements)); + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableProtoFile.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableProtoFile.java new file mode 100644 index 00000000..fbcee1c3 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableProtoFile.java @@ -0,0 +1,141 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.Extend; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.ProtoFile; +import com.squareup.wire.schema.Service; +import com.squareup.wire.schema.Type; + +/** Mutable builder analogue of {@link com.squareup.wire.schema.ProtoFile} used while generating proto files from XSD. */ +public class MutableProtoFile { + + private Location location; + private final List imports = new ArrayList<>(); + private final List publicImports = new ArrayList<>(); + private final String packageName; + private final List types = new ArrayList<>(); + // Extends and services are not produced by the XSD-to-proto path; they are carried through unchanged when modifying existing protos. + private final List extendList = new ArrayList<>(); + private final List services = new ArrayList<>(); + private final MutableOptions options; + private final Syntax syntax; + + public MutableProtoFile(Syntax syntax, String packageName) { + this.syntax = syntax; + this.packageName = packageName; + this.location = Location.get("", ""); + this.options = new MutableOptions(MutableOptions.FILE_OPTIONS, new ArrayList<>()); + } + + public List types() { + return types; + } + + public List imports() { + return imports; + } + + public List publicImports() { + return publicImports; + } + + public String packageName() { + return packageName; + } + + public MutableOptions options() { + return options; + } + + public List getExtendList() { + return extendList; + } + + public List getServices() { + return services; + } + + public Location location() { + return location; + } + + public void setLocation(Location location) { + this.location = location; + } + + public void mergeFrom(MutableProtoFile source) { + if (this.syntax != source.syntax) { + throw new IllegalArgumentException("Source and destination protos must follow the same syntax level (proto2/proto3)"); + } + + java.util.SortedSet mergedImports = new java.util.TreeSet<>(imports); + mergedImports.addAll(source.imports); + mergedImports.remove(location.getPath()); // Remove any imports to one self + imports.clear(); + imports.addAll(mergedImports); + + java.util.SortedSet mergedPublicImports = new java.util.TreeSet<>(publicImports); + mergedPublicImports.addAll(source.publicImports); + publicImports.clear(); + publicImports.addAll(mergedPublicImports); + + types.addAll(source.types); + extendList.addAll(source.extendList); + services.addAll(source.services); + } + + public ProtoFile toWire() { + List wireTypes = types.stream().map(t -> t.toWire(syntax)).collect(Collectors.toList()); + return new ProtoFile(location, new ArrayList<>(imports), new ArrayList<>(publicImports), Collections.emptyList() /* weakImports */, packageName, + wireTypes, new ArrayList<>(services), new ArrayList<>(extendList), options.toWire(), syntax); + } + + public String toSchema() { + return toWire().toSchema(); + } + + @Override + public String toString() { + return location.getPath(); + } + + public String name() { + String result = location.getPath(); + int slashIndex = result.lastIndexOf('/'); + if (slashIndex != -1) { + result = result.substring(slashIndex + 1); + } + if (result.endsWith(".proto")) { + result = result.substring(0, result.length() - ".proto".length()); + } + return result; + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableType.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableType.java new file mode 100644 index 00000000..7ac28eec --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/MutableType.java @@ -0,0 +1,48 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.List; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.Type; + +/** Mutable builder analogue of the stock wire {@link com.squareup.wire.schema.Type} hierarchy. */ +public abstract class MutableType { + + public abstract ProtoType type(); + + public abstract Location location(); + + public abstract String documentation(); + + public abstract void updateDocumentation(String documentation); + + public abstract MutableOptions options(); + + public abstract List nestedTypes(); + + public abstract Type toWire(Syntax syntax); +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireBuilders.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireBuilders.java new file mode 100644 index 00000000..5c1c2d11 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireBuilders.java @@ -0,0 +1,138 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.util.ArrayList; +import java.util.List; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.ProtoFile; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.Reserved; +import com.squareup.wire.schema.internal.parser.EnumConstantElement; +import com.squareup.wire.schema.internal.parser.EnumElement; +import com.squareup.wire.schema.internal.parser.FieldElement; +import com.squareup.wire.schema.internal.parser.MessageElement; +import com.squareup.wire.schema.internal.parser.OneOfElement; +import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import com.squareup.wire.schema.internal.parser.ReservedElement; +import com.squareup.wire.schema.internal.parser.TypeElement; + +/** + * Converts stock immutable wire types (via their element AST, {@link ProtoFile#toElement()}) into the mutable builder model used by schema2proto's + * post-processing and backwards-compatibility logic. Used by the proto-modification path, which loads existing protos with stock wire and then edits them. + */ +public final class WireBuilders { + + private WireBuilders() { + } + + public static MutableProtoFile fromProtoFile(ProtoFile protoFile) { + ProtoFileElement element = protoFile.toElement(); + Syntax syntax = element.getSyntax() != null ? element.getSyntax() : Syntax.PROTO_2; + String packageName = element.getPackageName(); + + MutableProtoFile file = new MutableProtoFile(syntax, packageName); + file.setLocation(element.getLocation()); + file.imports().addAll(element.getImports()); + file.publicImports().addAll(element.getPublicImports()); + file.options().optionElements().addAll(element.getOptions()); + // Carry extend declarations and services (gRPC RPCs) through unchanged; schema2proto does not modify them. + file.getExtendList().addAll(protoFile.getExtendList()); + file.getServices().addAll(protoFile.getServices()); + + for (TypeElement typeElement : element.getTypes()) { + file.types().add(fromType(typeElement, packageName)); + } + return file; + } + + private static MutableType fromType(TypeElement typeElement, String enclosing) { + if (typeElement instanceof MessageElement) { + return fromMessage((MessageElement) typeElement, enclosing); + } else if (typeElement instanceof EnumElement) { + return fromEnum((EnumElement) typeElement, enclosing); + } + throw new IllegalArgumentException("Unsupported type element: " + typeElement.getClass()); + } + + private static String qualify(String enclosing, String name) { + return enclosing == null || enclosing.isEmpty() ? name : enclosing + "." + name; + } + + private static MutableMessageType fromMessage(MessageElement element, String enclosing) { + String qualified = qualify(enclosing, element.getName()); + MutableOptions options = new MutableOptions(MutableOptions.MESSAGE_OPTIONS, new ArrayList<>(element.getOptions())); + MutableMessageType message = new MutableMessageType(ProtoType.get(qualified), element.getLocation(), element.getDocumentation(), element.getName(), + options); + + for (FieldElement fieldElement : element.getFields()) { + message.addField(fromField(fieldElement)); + } + for (OneOfElement oneOfElement : element.getOneOfs()) { + message.oneOfs().add(fromOneOf(oneOfElement)); + } + for (ReservedElement reservedElement : element.getReserveds()) { + message.getReserveds().add(fromReserved(reservedElement)); + } + for (TypeElement nested : element.getNestedTypes()) { + message.nestedTypes().add(fromType(nested, qualified)); + } + return message; + } + + private static MutableEnumType fromEnum(EnumElement element, String enclosing) { + String qualified = qualify(enclosing, element.getName()); + MutableOptions options = new MutableOptions(MutableOptions.ENUM_OPTIONS, new ArrayList<>(element.getOptions())); + List constants = new ArrayList<>(); + for (EnumConstantElement constantElement : element.getConstants()) { + constants.add(new MutableEnumConstant(constantElement.getLocation(), constantElement.getName(), constantElement.getTag(), + constantElement.getDocumentation(), new MutableOptions(MutableOptions.ENUM_VALUE_OPTIONS, new ArrayList<>(constantElement.getOptions())))); + } + List reserveds = new ArrayList<>(); + for (ReservedElement reservedElement : element.getReserveds()) { + reserveds.add(fromReserved(reservedElement)); + } + return new MutableEnumType(ProtoType.get(qualified), element.getLocation(), element.getDocumentation(), element.getName(), constants, reserveds, + options); + } + + private static MutableField fromField(FieldElement element) { + MutableOptions options = new MutableOptions(MutableOptions.FIELD_OPTIONS, new ArrayList<>(element.getOptions())); + return new MutableField(null, element.getLocation(), element.getLabel(), element.getName(), element.getDocumentation(), element.getTag(), + element.getDefaultValue(), element.getType(), options, false, false); + } + + private static MutableOneOf fromOneOf(OneOfElement element) { + MutableOptions options = new MutableOptions(MutableOptions.ONEOF_OPTIONS, new ArrayList<>(element.getOptions())); + List fields = new ArrayList<>(); + for (FieldElement fieldElement : element.getFields()) { + fields.add(fromField(fieldElement)); + } + return new MutableOneOf(element.getName(), element.getDocumentation(), fields, options); + } + + private static Reserved fromReserved(ReservedElement element) { + return new Reserved(element.getLocation(), element.getDocumentation(), element.getValues()); + } +} diff --git a/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireSchemaLoader.java b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireSchemaLoader.java new file mode 100644 index 00000000..a2a2d568 --- /dev/null +++ b/schema2proto-lib/src/main/java/no/entur/schema2proto/wire/WireSchemaLoader.java @@ -0,0 +1,93 @@ +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ +package no.entur.schema2proto.wire; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.Schema; +import com.squareup.wire.schema.SchemaLoader; + +/** + * Adapter that reproduces the vendored {@code SchemaLoader} contract (a set of source roots plus an optional set of specific protos to load) on top of stock + * wire's {@link SchemaLoader} ({@code initRoots} / {@code loadSchema}). + * + *

+ * When {@code protos} is empty all protos found under the source roots are loaded; otherwise only the named protos (and their imports) are loaded. Each proto + * is resolved against the first source root that contains it and is loaded only once, even when the same relative path exists in several roots (the vendored + * loader deduplicated the same way). This matters because extension definitions (e.g. {@code xsd/xsd.proto}) are frequently present in more than one root, and + * stock wire rejects a duplicated extension field. Google's well-known types (including {@code descriptor.proto}) are provided by stock wire automatically. + */ +public final class WireSchemaLoader { + + private WireSchemaLoader() { + } + + public static Schema load(List sources, List protos) throws IOException { + SchemaLoader loader = new SchemaLoader(FileSystems.getDefault()); + + // Map each relative proto path to the first source root that contains it (first root wins; deduplicates across roots). + Map protoToRoot = new LinkedHashMap<>(); + for (Path root : sources) { + if (!Files.isDirectory(root)) { + throw new IllegalArgumentException("Source root is not a directory: " + root); + } + try (Stream walk = Files.walk(root)) { + walk.filter(p -> p.toString().endsWith(".proto")).forEach(p -> protoToRoot.putIfAbsent(root.relativize(p).toString().replace('\\', '/'), root)); + } + } + + Set loadSet = protos.isEmpty() ? new LinkedHashSet<>(protoToRoot.keySet()) : new LinkedHashSet<>(protos); + + List sourcePath = new ArrayList<>(); + for (String proto : loadSet) { + Path root = protoToRoot.get(proto); + if (root == null) { + throw new java.io.FileNotFoundException("Failed to locate " + proto + " in " + sources); + } + sourcePath.add(Location.get(root.toString(), proto)); + } + + // Remaining protos are available for transitive imports, added as individual (deduplicated) files so the same path is never offered twice. + List protoPath = new ArrayList<>(); + for (Map.Entry entry : protoToRoot.entrySet()) { + if (!loadSet.contains(entry.getKey())) { + protoPath.add(Location.get(entry.getValue().toString(), entry.getKey())); + } + } + + loader.initRoots(sourcePath, protoPath); + return loader.loadSchema(); + } + +} diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/ProtoComparator.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/ProtoComparator.java index 1b2f4038..18ce06e7 100644 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/ProtoComparator.java +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/ProtoComparator.java @@ -45,10 +45,11 @@ import com.squareup.wire.schema.ProtoMember; import com.squareup.wire.schema.Rpc; import com.squareup.wire.schema.Schema; -import com.squareup.wire.schema.SchemaLoader; import com.squareup.wire.schema.Service; import com.squareup.wire.schema.Type; +import no.entur.schema2proto.wire.WireSchemaLoader; + public class ProtoComparator { public static void compareProtoFiles(File expectedRootFolder, File expectedFile, File generatedRootFolder, File generatedFile) { @@ -57,14 +58,15 @@ public static void compareProtoFiles(File expectedRootFolder, File expectedFile, ProtoFile generated = load(generatedRootFolder, generatedFile); assertEquals(expected.javaPackage(), generated.javaPackage(), - "java package mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.packageName(), generated.packageName(), - "package name mismatch" + generateLocationInformation(expected.location(), generated.location())); - // assertEquals(expected.name(), generated.name(), "name mismatch" + generateLocationInformation(expected.location(), generated.location())); + "java package mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getPackageName(), generated.getPackageName(), + "package name mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + // assertEquals(expected.getName(), generated.getName(), "name mismatch" + generateLocationInformation(expected.getLocation(), + // generated.getLocation())); - compareOptions(expected.options(), generated.options()); + compareOptions(expected.getOptions(), generated.getOptions()); compareServices(expected, generated); - compareTypes(expected, generated, expected.types(), generated.types()); + compareTypes(expected, generated, expected.getTypes(), generated.getTypes()); } catch (IOException e) { fail("Error loading protos for comparison", e); } @@ -76,11 +78,11 @@ private static void compareServices(ProtoFile expected, ProtoFile generated) { Map expectedMap = new HashMap<>(); Map generatedMap = new HashMap<>(); - for (Service t : expected.services()) { + for (Service t : expected.getServices()) { expectedMap.put(t.name(), t); } - for (Service t : generated.services()) { + for (Service t : generated.getServices()) { generatedMap.put(t.name(), t); } @@ -113,11 +115,11 @@ private static void compareRpcs(Service expected, Service generated) { Map generatedMap = new HashMap<>(); for (Rpc t : expected.rpcs()) { - expectedMap.put(t.name(), t); + expectedMap.put(t.getName(), t); } for (Rpc t : generated.rpcs()) { - generatedMap.put(t.name(), t); + generatedMap.put(t.getName(), t); } Set missing = new HashSet<>(expectedMap.keySet()); @@ -135,33 +137,34 @@ private static void compareRpcs(Service expected, Service generated) { } private static void compareRpc(Rpc expected, Rpc generated) { - assertEquals(expected.documentation(), generated.documentation(), - "Rpc call documentation mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.name(), generated.name(), "Rpc call name mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.requestStreaming(), generated.requestStreaming(), - "Rpc call request streaming mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.requestType(), generated.requestType(), - "Rpc call request type mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.responseStreaming(), generated.responseStreaming(), - "Rpc call response streaming mismatch" + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.responseType(), generated.responseType(), - "Rpc call response type mismatch" + generateLocationInformation(expected.location(), generated.location())); - - compareOptions(expected.options(), generated.options()); + assertEquals(expected.getDocumentation(), generated.getDocumentation(), + "Rpc call documentation mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getName(), generated.getName(), + "Rpc call name mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getRequestStreaming(), generated.getRequestStreaming(), + "Rpc call request streaming mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getRequestType(), generated.getRequestType(), + "Rpc call request type mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getResponseStreaming(), generated.getResponseStreaming(), + "Rpc call response streaming mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getResponseType(), generated.getResponseType(), + "Rpc call response type mismatch" + generateLocationInformation(expected.getLocation(), generated.getLocation())); + + compareOptions(expected.getOptions(), generated.getOptions()); } private static void compareOptions(Options expected, Options generated) { - Set missing = new HashSet<>(expected.map().keySet()); - missing.removeAll(generated.map().keySet()); + Set missing = new HashSet<>(expected.getMap().keySet()); + missing.removeAll(generated.getMap().keySet()); assertTrue(missing.isEmpty(), "Options not found in generated proto file: " + missing); - Set unexpected = new HashSet<>(generated.map().keySet()); - unexpected.removeAll(expected.map().keySet()); + Set unexpected = new HashSet<>(generated.getMap().keySet()); + unexpected.removeAll(expected.getMap().keySet()); assertTrue(unexpected.isEmpty(), "Unexpected options found in generated proto file: " + unexpected); - for (Entry expectedService : expected.map().entrySet()) { - Object generatedType = generated.map().get(expectedService.getKey()); + for (Entry expectedService : expected.getMap().entrySet()) { + Object generatedType = generated.getMap().get(expectedService.getKey()); assertEquals(expectedService.getValue(), generatedType, "Options value mismatch for " + expectedService.getKey()); } } @@ -172,12 +175,12 @@ private static void compareTypes(ProtoFile expected, ProtoFile generated, List generatedTypes = new HashMap<>(); for (Type t : _expectedTypes) { - expectedTypes.put(t.type().simpleName(), t); + expectedTypes.put(t.getType().getSimpleName(), t); } for (Type t : _generatedTypes) { - generatedTypes.put(t.type().simpleName(), t); + generatedTypes.put(t.getType().getSimpleName(), t); } Set missingTypes = new HashSet<>(expectedTypes.keySet()); @@ -201,10 +204,10 @@ private static void compareTypes(ProtoFile expected, ProtoFile generated, List generatedFields = new HashMap<>(); for (Field t : expected.fields()) { - expectedFields.put(t.name(), t); + expectedFields.put(t.getName(), t); } for (Field t : generated.fields()) { - generatedFields.put(t.name(), t); + generatedFields.put(t.getName(), t); } Set missingFields = new HashSet<>(expectedFields.keySet()); @@ -302,37 +305,56 @@ private static void compareFields(MessageType expected, MessageType generated) { } private static void compareField(Field expected, Field generated) { - assertEquals(expected.documentation(), generated.documentation(), - "Field documentation mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.name(), generated.name(), - "Field name mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + assertEquals(expected.getDocumentation(), generated.getDocumentation(), + "Field documentation mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getName(), generated.getName(), + "Field name mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isRepeated(), generated.isRepeated(), - "Field isRepeated mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isRepeated mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isDeprecated(), generated.isDeprecated(), - "Field isDeprecated mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isDeprecated mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isExtension(), generated.isExtension(), - "Field isExtension mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.isOptional(), generated.isOptional(), - "Field isOptional mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isExtension mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getLabel(), generated.getLabel(), + "Field isOptional mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isPacked(), generated.isPacked(), - "Field isPacked mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isPacked mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isRequired(), generated.isRequired(), - "Field isRequired mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isRequired mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); assertEquals(expected.isRedacted(), generated.isRedacted(), - "Field isRedacted mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.tag(), generated.tag(), - "Field tag mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); - assertEquals(expected.type().simpleName(), generated.type().simpleName(), - "Field type mismatch " + expected.toString() + generateLocationInformation(expected.location(), generated.location())); + "Field isRedacted mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getTag(), generated.getTag(), + "Field tag mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); + assertEquals(expected.getType().getSimpleName(), generated.getType().getSimpleName(), + "Field type mismatch " + expected.toString() + generateLocationInformation(expected.getLocation(), generated.getLocation())); } private static ProtoFile load(File rootFolder, File protoFilename) throws IOException { - SchemaLoader schemaLoader = new SchemaLoader(); - schemaLoader.addSource(rootFolder); - schemaLoader.addProto(protoFilename.getPath()); - Schema schema = schemaLoader.load(); - ProtoFile protofile = schema.protoFile(protoFilename.getPath()); - return protofile; + List sources = new java.util.ArrayList<>(); + sources.add(rootFolder.toPath()); + + // Stock wire links options strictly: the option-extension definitions (buf/validate, xsd) must be compiled as sources, not + // merely on the import path. When the file's own root does not contain validate.proto, pull it from the unpacked proto + // dependencies (only then, to avoid duplicate-path ambiguity). + boolean rootHasValidate = new File(rootFolder, "buf/validate/validate.proto").exists(); + File protoDeps = new File("target/proto_deps"); + if (!rootHasValidate && protoDeps.exists()) { + sources.add(protoDeps.toPath()); + } + + List protos = new java.util.ArrayList<>(); + protos.add(protoFilename.getPath()); + for (String optionProto : new String[] { "buf/validate/validate.proto", "xsd/xsd.proto" }) { + for (java.nio.file.Path s : sources) { + if (java.nio.file.Files.exists(s.resolve(optionProto))) { + protos.add(optionProto); + break; + } + } + } + + Schema schema = WireSchemaLoader.load(sources, protos); + return schema.protoFile(protoFilename.getPath()); } private static String generateLocationInformation(Location expected, Location generated) { diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/compatibility/AbstractBackwardsCompatTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/compatibility/AbstractBackwardsCompatTest.java index db61355a..4963c9d4 100644 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/compatibility/AbstractBackwardsCompatTest.java +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/compatibility/AbstractBackwardsCompatTest.java @@ -9,12 +9,12 @@ * Licensed under the EUPL, Version 1.1 or – as soon they will be * approved by the European Commission - subsequent versions of the * EUPL (the "Licence"); - * + * * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * http://ec.europa.eu/idabc/eupl5 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,16 +31,16 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; -import java.util.Iterator; -import java.util.Locale; +import java.util.Collections; -import org.jetbrains.annotations.NotNull; +import org.apache.commons.io.FileUtils; -import com.squareup.wire.schema.Linker; -import com.squareup.wire.schema.Location; import com.squareup.wire.schema.ProtoFile; import com.squareup.wire.schema.Schema; -import com.squareup.wire.schema.SchemaLoader; + +import no.entur.schema2proto.wire.MutableProtoFile; +import no.entur.schema2proto.wire.WireBuilders; +import no.entur.schema2proto.wire.WireSchemaLoader; public abstract class AbstractBackwardsCompatTest { @@ -51,30 +51,26 @@ public abstract class AbstractBackwardsCompatTest { protected void verify(String testname, boolean failOnRemovedFields, String protoFile) throws IOException { ProtolockBackwardsCompatibilityChecker checker = new ProtolockBackwardsCompatibilityChecker(); - checker.init(new File(testdataBaseDirectory + "/" + testname + "/" + sourceFolder + "/" + lockFile)); + File sourceDir = new File(testdataBaseDirectory + "/" + testname + "/" + sourceFolder); + checker.init(new File(sourceDir, lockFile)); - Schema sourceSchema = loadSchema(new File(testdataBaseDirectory + "/" + testname + "/" + sourceFolder)); - link(sourceSchema, false, testname); + Schema sourceSchema = loadSchema(sourceDir); ProtoFile sourceProtofile = sourceSchema.protoFile(protoFile); - boolean backwardsIncompatibiltyDetected = checker.resolveBackwardIncompatibilities(sourceProtofile); + // schema2proto resolves backwards incompatibilities on its mutable builder model + MutableProtoFile sourceBuilder = WireBuilders.fromProtoFile(sourceProtofile); + boolean backwardsIncompatibiltyDetected = checker.resolveBackwardIncompatibilities(sourceBuilder); + + String generated = sourceBuilder.toSchema(); - // Verify that schema still links - boolean linkedOk = link(sourceSchema, true, testname); - assertTrue(linkedOk); + // Verify the mutated schema still links (is valid proto) + assertTrue(linksOk(sourceDir, protoFile, generated, testname), "Resolved schema does not link"); Schema expectedSchema = loadSchema(new File(testdataBaseDirectory + "/" + testname + "/" + expectedFolder)); - link(expectedSchema, false, testname); ProtoFile expectedProtofile = expectedSchema.protoFile(protoFile); - // Remove file location comment as it should be the only difference between the two files - sourceProtofile.setLocation(new Location("", "", -1, -1)); - expectedProtofile.setLocation(new Location("", "", -1, -1)); - - // System.out.println("Expected\n" + expectedProtofile.toSchema()); - // System.out.println("Actual\n" + sourceProtofile.toSchema()); - - assertEquals(expectedProtofile.toSchema(), sourceProtofile.toSchema()); + // Ignore the leading "// " location comment, which is the only expected difference + assertEquals(stripLocationComment(expectedProtofile.toSchema()), stripLocationComment(generated)); if (failOnRemovedFields) { assertFalse(backwardsIncompatibiltyDetected); @@ -82,59 +78,40 @@ protected void verify(String testname, boolean failOnRemovedFields, String proto } private Schema loadSchema(File path) throws IOException { - SchemaLoader schemaLoader = new SchemaLoader(); - schemaLoader.addSource(path); - return schemaLoader.load(); + return WireSchemaLoader.load(Collections.singletonList(path.toPath()), Collections.emptyList()); } - private boolean link(Schema schema, boolean dumpIfNotLinkable, String testname) throws IOException { - Linker linker = new Linker(schema.protoFiles()); + private boolean linksOk(File sourceDir, String protoFile, String generated, String testname) throws IOException { + File tmp = new File("target/compatibilitytest_dump/" + testname); + FileUtils.deleteDirectory(tmp); + FileUtils.copyDirectory(sourceDir, tmp); + File target = new File(tmp, protoFile); + target.getParentFile().mkdirs(); + try (Writer writer = new FileWriter(target)) { + writer.write(generated); + } try { - linker.link(); + WireSchemaLoader.load(Collections.singletonList(tmp.toPath()), Collections.emptyList()); + return true; } catch (Exception e) { - System.out.println("Linking failed, the proto file is not valid" + e); - if (dumpIfNotLinkable) { - File dumpFolder = new File("target/compatibilitytest_dump"); - dumpFolder.mkdirs(); - System.out.println("Dumpfolder: " + dumpFolder.getAbsolutePath()); - for (ProtoFile protoFile : schema.protoFiles()) { - File destFolder = createPackageFolderStructure(new File(dumpFolder, testname), protoFile.packageName()); - File outputFile = new File(destFolder, protoFile.name().toLowerCase(Locale.ROOT)); - try (Writer writer = new FileWriter(outputFile)) { - writer.write(protoFile.toSchema()); - } - } - - } + System.out.println("Linking failed, the proto file is not valid: " + e); return false; } - - return true; - } - - private File createPackageFolderStructure(File outputDirectory, String packageName) { - - String folderSubstructure = getPathFromPackageName(packageName); - File dstFolder = new File(outputDirectory, folderSubstructure); - dstFolder.mkdirs(); - - return dstFolder; - - } - - @NotNull - private String getPathFromPackageName(String packageName) { - return packageName.replace('.', '/'); } - public static Iterable getIterableFromIterator(Iterator iterator) { - return new Iterable() { - - @Override - public Iterator iterator() { - return iterator; - } - }; + private static String stripLocationComment(String schema) { + int newline = schema.indexOf('\n'); + if (newline == -1) + return schema; + String firstLine = schema.substring(0, newline); + if (firstLine.startsWith("// Proto schema formatted by Wire") || firstLine.startsWith("// Source:")) { + return schema; + } + // Strip only legacy location comments like "// default.proto at 0:0" or "// /abs/path/file.proto". + if (firstLine.startsWith("//") && firstLine.contains(".proto")) { + return schema.substring(newline + 1); + } + return schema; } } diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/generateproto/GenerateValidationRulesTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/generateproto/GenerateValidationRulesTest.java index defd9c2e..7d14a1b5 100644 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/generateproto/GenerateValidationRulesTest.java +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/generateproto/GenerateValidationRulesTest.java @@ -75,11 +75,11 @@ public void generateProtobuf_whenAttributeRestrictions_thenOnlyAddStringOptionsF // Test that only recognized facets (like minLength, but not enumeration) generate validation options in the protobuf output. generateProtobuf("test-attribute-restrictions.xsd", validationOptions()); String generated = IOUtils.toString(Files.newInputStream(Paths.get("target/generated-proto/default/default.proto")), Charset.defaultCharset()); - Assertions.assertEquals("// default.proto at 0:0\n" + "syntax = \"proto3\";\n" + "package default;\n" + "\n" - + "import \"buf/validate/validate.proto\";\n" + "\n" + "enum EnumType {\n" + " // Default\n" + " ENUM_TYPE_UNSPECIFIED = 0;\n" - + " ENUM_TYPE_VALUE_1 = 1;\n" + " ENUM_TYPE_VALUE_2 = 2;\n" + "}\n" + "message TypeWithEnum {\n" + " EnumType enum_attribute = 1;\n" - + " string min_length_attribute = 2 [\n" + " (buf.validate.field).string = {\n" + " min_len: 2\n" + " }\n" + " ];\n" + "}\n", - generated); + Assertions.assertEquals("// Proto schema formatted by Wire, do not edit.\n" + "// Source: default.proto\n" + "\n" + "syntax = \"proto3\";\n" + "\n" + + "package default;\n" + "\n" + "import \"buf/validate/validate.proto\";\n" + "\n" + "enum EnumType {\n" + " // Default\n" + + " ENUM_TYPE_UNSPECIFIED = 0;\n" + " ENUM_TYPE_VALUE_1 = 1;\n" + " ENUM_TYPE_VALUE_2 = 2;\n" + "}\n" + "\n" + "message TypeWithEnum {\n" + + " EnumType enum_attribute = 1;\n" + "\n" + " string min_length_attribute = 2 [(buf.validate.field).string = {\n" + " min_len: 2\n" + + " }];\n" + "}\n", generated); } private Schema2ProtoConfiguration validationOptions() { diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/modifyproto/ModifyProtoTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/modifyproto/ModifyProtoTest.java index 7a5e3cdb..c6712bdd 100644 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/modifyproto/ModifyProtoTest.java +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/modifyproto/ModifyProtoTest.java @@ -23,6 +23,7 @@ package no.entur.schema2proto.modifyproto; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -106,6 +107,58 @@ public void testWhitelistMessageTypeWithIncludeBaseTypeOptionDisabled() throws I } + @Test + public void testIncludeAndExcludeOverlapWithBaseTypes() throws IOException, InvalidProtobufException, InvalidConfigurationException { + // Regression: when followOneMoreLevel adds a base type ("A", referenced by B's xsd.base_type) that is also excluded, + // the identifier ends up in both roots and prunes. The vendored IdentifierSet tolerated this (excludes win); stock + // wire's PruningRules rejects the overlap. ModifyProto must drop the overlap from the roots rather than fail. + File source = new File("src/test/resources/modify/input/xsdbasetype").getCanonicalFile(); + + ModifyProtoConfiguration configuration = new ModifyProtoConfiguration(); + configuration.inputDirectory = source; + configuration.includes = Collections.singletonList("B"); + configuration.excludes = Collections.singletonList("A"); + configuration.includeBaseTypes = true; + + modifyProto(configuration); + + assertTrue(new File(generatedRootFolder, "simple.proto").exists(), "Expected proto to be generated without a roots/prunes overlap failure"); + } + + @Test + public void testDuplicateOptionDefinitionAcrossSourceRootsIsDeduplicated() throws IOException, InvalidProtobufException, InvalidConfigurationException { + // Regression: the xsd.base_type extension definition (xsd/xsd.proto) is commonly present in more than one source root + // (e.g. unpacked proto deps and the input dir). Loading it from each root would define the extension twice, which stock + // wire rejects ("multiple fields share tag/name"). The loader must deduplicate protos by path (first root wins). + File source = new File("src/test/resources/modify/input/xsdbasetype").getCanonicalFile(); + + ModifyProtoConfiguration configuration = new ModifyProtoConfiguration(); + configuration.inputDirectory = source; + // A second source root that also contains xsd/xsd.proto (mirrors unpacked proto deps alongside the input dir). + configuration.customImportLocations = Collections.singletonList("src/test/resources/modify/dupimport"); + configuration.includes = Collections.singletonList("B"); + configuration.includeBaseTypes = true; + + modifyProto(configuration); + + assertTrue(new File(generatedRootFolder, "simple.proto").exists(), "Expected proto to be generated despite duplicate xsd.proto across source roots"); + } + + @Test + public void testServicesAndRpcsArePreserved() throws IOException, InvalidProtobufException, InvalidConfigurationException { + // Regression: gRPC service/RPC declarations on existing protos must survive the modify round-trip (the builder model + // previously dropped services, producing "missing RPC" protolock conflicts downstream). + File expected = new File("src/test/resources/modify/expected/withservice").getCanonicalFile(); + File source = new File("src/test/resources/modify/input/withservice").getCanonicalFile(); + + ModifyProtoConfiguration configuration = new ModifyProtoConfiguration(); + configuration.inputDirectory = source; + + modifyProto(configuration); + + compareExpectedAndGenerated(expected, "svc.proto", generatedRootFolder, "svc.proto"); + } + @Test public void testRemoveFieldAndType() throws IOException, InvalidProtobufException, InvalidConfigurationException { diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/ParserTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/wire/ParserTest.java similarity index 53% rename from schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/ParserTest.java rename to schema2proto-lib/src/test/java/no/entur/schema2proto/wire/ParserTest.java index a18e0b99..b8932bce 100644 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/ParserTest.java +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/wire/ParserTest.java @@ -1,4 +1,4 @@ -package no.entur.schema2proto.wiretest; +package no.entur.schema2proto.wire; /*- * #%L @@ -25,37 +25,33 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import com.squareup.wire.schema.IdentifierSet; import com.squareup.wire.schema.ProtoFile; +import com.squareup.wire.schema.PruningRules; import com.squareup.wire.schema.Schema; -import com.squareup.wire.schema.SchemaLoader; public class ParserTest { @Test public void testParseWithFieldRules() throws IOException { - SchemaLoader schemaLoader = new SchemaLoader(); - schemaLoader.addSource(new File("src/test/resources/wiretest/source")); - schemaLoader.addSource(new File("target/proto_deps")); - schemaLoader.addProto("packagename/fieldrulesloading.proto"); - Schema schema = schemaLoader.load(); + Schema schema = WireSchemaLoader.load(Arrays.asList(new File("src/test/resources/wiretest/source").toPath(), new File("target/proto_deps").toPath()), + Collections.singletonList("packagename/fieldrulesloading.proto")); - IdentifierSet.Builder b = new IdentifierSet.Builder(); - b.exclude("packagename.PruneMessage"); - Schema prunedSchema = schema.prune(b.build()); + PruningRules rules = new PruningRules.Builder().prune("packagename.PruneMessage").build(); + Schema prunedSchema = schema.prune(rules); ProtoFile protoFile = prunedSchema.protoFile("packagename/fieldrulesloading.proto"); String prunedFile = protoFile.toSchema(); - Assertions.assertEquals( - "// src/test/resources/wiretest/source/packagename/fieldrulesloading.proto\n" + "syntax = \"proto3\";\n" + "package packagename;\n" + "\n" - + "import \"buf/validate/validate.proto\";\n" + "\n" + "message PriceUnit {\n" + " SubMessage with_options_nested_style = 1 [\n" - + " (buf.validate.field).required = true\n" + " ];\n" + "}\n" + "message SubMessage {\n" + " string x = 1;\n" + "}\n", - prunedFile); + Assertions.assertEquals("// Proto schema formatted by Wire, do not edit.\n" + "// Source: packagename/fieldrulesloading.proto\n" + "\n" + + "syntax = \"proto3\";\n" + "\n" + "package packagename;\n" + "\n" + "import \"buf/validate/validate.proto\";\n" + "\n" + + "message PriceUnit {\n" + " SubMessage with_options_nested_style = 1 [(buf.validate.field).required = true];\n" + "}\n" + "\n" + + "message SubMessage {\n" + " string x = 1;\n" + "}\n", prunedFile); } } diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/wire/SerializationTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/wire/SerializationTest.java new file mode 100644 index 00000000..12d6486a --- /dev/null +++ b/schema2proto-lib/src/test/java/no/entur/schema2proto/wire/SerializationTest.java @@ -0,0 +1,85 @@ +package no.entur.schema2proto.wire; + +/*- + * #%L + * schema2proto-lib + * %% + * Copyright (C) 2019 Entur + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.squareup.wire.Syntax; +import com.squareup.wire.schema.Extend; +import com.squareup.wire.schema.Field; +import com.squareup.wire.schema.Location; +import com.squareup.wire.schema.Options; +import com.squareup.wire.schema.ProtoFile; +import com.squareup.wire.schema.ProtoType; +import com.squareup.wire.schema.internal.parser.OptionElement; + +public class SerializationTest { + + /** Builds a proto file through schema2proto's mutable builder model and verifies it serializes via stock wire. */ + @Test + public void testBuildProtoFile() { + + MutableProtoFile f = new MutableProtoFile(Syntax.PROTO_3, "default"); + + Location valueLocation = new Location("base", "file", 1, 2); + List values = new ArrayList<>(); + values.add(new MutableEnumConstant(valueLocation, "VAL1", 1, "doc", new MutableOptions(MutableOptions.ENUM_VALUE_OPTIONS, new ArrayList<>()))); + f.types() + .add(new MutableEnumType(ProtoType.get("Name"), valueLocation, "doc", "Name", values, new ArrayList<>(), + new MutableOptions(MutableOptions.ENUM_OPTIONS, new ArrayList<>()))); + + Location messageLocation = new Location("base", "path", 1, 1); + f.types() + .add(new MutableMessageType(ProtoType.get("messagename"), messageLocation, "doc", "messagename", + new MutableOptions(MutableOptions.MESSAGE_OPTIONS, new ArrayList<>()))); + + String schema = f.toSchema(); + assertNotNull(schema); + } + + /** Verifies stock wire serializes extend declarations (used when modifying existing protos that contain them). */ + @Test + public void testBuildExtension() { + Location loc = new Location("", "", 0, 0); + + List optionElements = new ArrayList<>(); + Options options = new Options(ProtoType.get("google.protobuf.MessageOptions"), optionElements); + Field field = new Field(Collections.emptyList(), loc, null, "fieldname", "Base type this message actually is an extension of", 1101, null, "string", + options, true, false, null); + Extend extend = new Extend(loc, "Information elements extracted from the xsd structure", "google.protobuf.MessageOptions", + Collections.singletonList(field)); + + ProtoFile f = new ProtoFile(loc, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), "default", new ArrayList<>(), new ArrayList<>(), + Collections.singletonList(extend), new Options(Options.FILE_OPTIONS, new ArrayList<>()), Syntax.PROTO_3); + + String schema = f.toSchema(); + assertNotNull(schema); + } +} diff --git a/schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/SerializationTest.java b/schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/SerializationTest.java deleted file mode 100644 index bd7b08a6..00000000 --- a/schema2proto-lib/src/test/java/no/entur/schema2proto/wiretest/SerializationTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package no.entur.schema2proto.wiretest; - -/*- - * #%L - * schema2proto-lib - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.EnumConstant; -import com.squareup.wire.schema.EnumType; -import com.squareup.wire.schema.Extend; -import com.squareup.wire.schema.Extensions; -import com.squareup.wire.schema.Field; -import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.MessageType; -import com.squareup.wire.schema.OneOf; -import com.squareup.wire.schema.Options; -import com.squareup.wire.schema.ProtoFile; -import com.squareup.wire.schema.ProtoFile.Syntax; -import com.squareup.wire.schema.ProtoType; -import com.squareup.wire.schema.Reserved; -import com.squareup.wire.schema.Type; -import com.squareup.wire.schema.internal.parser.OptionElement; - -public class SerializationTest { - @Test - public void testBuildProtoFile() { - - ProtoFile f = new ProtoFile(Syntax.PROTO_3, "default"); - - List values = new ArrayList(); - List elements = new ArrayList<>(); - List reserveds = new ArrayList<>(); - Options constantOptions = new Options(ProtoType.BOOL, elements); - - Location valueLocation = new Location("base", "file", 1, 2); - values.add(new EnumConstant(valueLocation, "VAL1", 1, "doc", constantOptions)); - Options options = new Options(ProtoType.BOOL, new ArrayList<>()); - f.types().add(new EnumType(ProtoType.INT32, valueLocation, "doc", "Name", values, reserveds, options)); - - Location messagLocation = new Location("base", "path", 1, 1); - ImmutableList declaredFields = ImmutableList.copyOf(new ArrayList<>()); - List extensionFields = new ArrayList<>(); - - ImmutableList oneOfs = ImmutableList.copyOf(new ArrayList<>()); - - ImmutableList nestedTypes = ImmutableList.copyOf(new ArrayList<>()); - - List extensionsList = new ArrayList<>(); - - List messageOptionElements = new ArrayList<>(); - Options messageOptions = new Options(ProtoType.BOOL, messageOptionElements); - MessageType message = new MessageType(ProtoType.BOOL, messagLocation, "doc", "messagename", declaredFields, extensionFields, oneOfs, nestedTypes, - extensionsList, reserveds, messageOptions); - f.types().add(message); - - String schema = f.toSchema(); - assertNotNull(schema); - } - - @Test - public void testBuildExtension() { - ProtoFile f = new ProtoFile(Syntax.PROTO_3, "default"); - - Location loc = new Location("", "", 0, 0); - - List fields = new ArrayList<>(); - List optionElements = new ArrayList<>(); - Options options = new Options(ProtoType.get("google.protobuf.MessageOptions"), optionElements); - Field field = new Field(null, loc, null, "fieldname", "Base type this message actually is an extension of", 1101, "string", options, false); - fields.add(field); - Extend e = new Extend(loc, "Information elements extracted from the xsd structure", "google.protobuf.MessageOptions", fields); - f.getExtendList().add(e); - - String schema = f.toSchema(); - System.out.println(schema); - assertNotNull(schema); - - } -} diff --git a/schema2proto-lib/src/test/resources/modify/dupimport/xsd/xsd.proto b/schema2proto-lib/src/test/resources/modify/dupimport/xsd/xsd.proto new file mode 100644 index 00000000..314a68b4 --- /dev/null +++ b/schema2proto-lib/src/test/resources/modify/dupimport/xsd/xsd.proto @@ -0,0 +1,11 @@ +// xsd.proto at 0:0 +syntax = "proto3"; +package xsd; + +import "google/protobuf/descriptor.proto"; + +// Information elements extracted from the xsd structure +extend google.protobuf.MessageOptions { + // Base type this message actually is an extension of + string base_type = 1101; +} diff --git a/schema2proto-lib/src/test/resources/modify/expected/nopackagename/addedFieldOption.proto b/schema2proto-lib/src/test/resources/modify/expected/nopackagename/addedFieldOption.proto index 0c010b72..f464bd69 100644 --- a/schema2proto-lib/src/test/resources/modify/expected/nopackagename/addedFieldOption.proto +++ b/schema2proto-lib/src/test/resources/modify/expected/nopackagename/addedFieldOption.proto @@ -1,5 +1,6 @@ // default.proto at 0:0 syntax = "proto3"; +import "buf/validate/validate.proto"; // Type for Status of termination response. message A { diff --git a/schema2proto-lib/src/test/resources/modify/expected/nopackagename/modifyfield.proto b/schema2proto-lib/src/test/resources/modify/expected/nopackagename/modifyfield.proto index e9333916..10bd236c 100644 --- a/schema2proto-lib/src/test/resources/modify/expected/nopackagename/modifyfield.proto +++ b/schema2proto-lib/src/test/resources/modify/expected/nopackagename/modifyfield.proto @@ -1,5 +1,6 @@ // default.proto at 0:0 syntax = "proto3"; +import "buf/validate/validate.proto"; // Type for Status of termination response. message A { diff --git a/schema2proto-lib/src/test/resources/modify/expected/withservice/svc.proto b/schema2proto-lib/src/test/resources/modify/expected/withservice/svc.proto new file mode 100644 index 00000000..43eb673b --- /dev/null +++ b/schema2proto-lib/src/test/resources/modify/expected/withservice/svc.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package svc; + +message Req { + string a = 1; +} +message Resp { + string b = 1; +} +service S { + rpc DoIt(Req) returns (Resp); +} diff --git a/schema2proto-lib/src/test/resources/modify/expected/xsdbasetype/disabled.proto b/schema2proto-lib/src/test/resources/modify/expected/xsdbasetype/disabled.proto index 0e845730..a223851f 100644 --- a/schema2proto-lib/src/test/resources/modify/expected/xsdbasetype/disabled.proto +++ b/schema2proto-lib/src/test/resources/modify/expected/xsdbasetype/disabled.proto @@ -1,5 +1,6 @@ // /home/erlend/Projects/abt/schema2proto/schema2proto-lib/src/test/resources/modify/input/xsdbasetype/simple.proto syntax = "proto3"; +import "xsd/xsd.proto"; message B { option (xsd.base_type) = "A"; diff --git a/schema2proto-lib/src/test/resources/modify/input/withservice/svc.proto b/schema2proto-lib/src/test/resources/modify/input/withservice/svc.proto new file mode 100644 index 00000000..43eb673b --- /dev/null +++ b/schema2proto-lib/src/test/resources/modify/input/withservice/svc.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package svc; + +message Req { + string a = 1; +} +message Resp { + string b = 1; +} +service S { + rpc DoIt(Req) returns (Resp); +} diff --git a/schema2proto-wire/pom.xml b/schema2proto-wire/pom.xml deleted file mode 100755 index 916a49eb..00000000 --- a/schema2proto-wire/pom.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - 4.0.0 - - no.entur - schema2proto-parent - 0.1-SNAPSHOT - - schema2proto-wire - 0.1-SNAPSHOT - jar - schema2proto-wire - schema2proto-wire - - - org.jetbrains.kotlin - kotlin-stdlib - - - com.google.guava - guava - - - - com.google.protobuf - protobuf-java - runtime - - - org.apache.commons - commons-lang3 - compile - - - com.squareup.wire - wire-runtime-jvm - - - com.squareup.okio - okio - - - org.assertj - assertj-core - test - - - io.github.java-diff-utils - java-diff-utils - test - - - org.junit.jupiter - junit-jupiter - test - - - com.google.jimfs - jimfs - test - - - - - - com.diffplug.spotless - spotless-maven-plugin - - - - ../codestyle/jdt_codestyle.xml - 4.9.0 - - - - ../codestyle/jdt.importorder - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - - compile - - - - ${project.basedir}/src/main/java - - - - - test-compile - - test-compile - - - - ${project.basedir}/src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - default-compile - none - - - - default-testCompile - none - - - java-compile - - compile - - compile - - - java-test-compile - - testCompile - - test-compile - - - - - - https://github.com/entur/schema2proto - diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnclosingType.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnclosingType.kt deleted file mode 100644 index 68310e76..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnclosingType.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.internal.parser.MessageElement - -/** An empty type which only holds nested types. */ -class EnclosingType internal constructor( - private val location: Location, - private val type: ProtoType, - private val documentation: String, - private val nestedTypes: List -) : Type() { - // TODO(jrodbx): Konvert to overridden vals, once Type is konverted - override fun location() = location - - override fun type() = type - override fun documentation() = documentation - override fun options() = throw UnsupportedOperationException() - override fun nestedTypes() = nestedTypes - - internal override fun link(linker: Linker) = nestedTypes.forEach { it.link(linker) } - internal override fun linkOptions(linker: Linker) = nestedTypes.forEach { it.linkOptions(linker) } - internal override fun validate(linker: Linker) = nestedTypes.forEach { it.validate(linker) } - - internal override fun retainAll( - schema: Schema, - markSet: MarkSet - ): Type? { - val retainedNestedTypes = nestedTypes.mapNotNull { it.retainAll(schema, markSet) } - return if (retainedNestedTypes.isEmpty()) null - else EnclosingType(location, type, documentation, retainedNestedTypes) - } - - fun toElement() = MessageElement( - location = location, - name = type.simpleName(), - nestedTypes = Type.toElements(nestedTypes) - ) -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumConstant.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumConstant.kt deleted file mode 100644 index e20a1562..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumConstant.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.internal.parser.EnumConstantElement - -class EnumConstant public constructor( - val location: Location, - var name: String, - var tag: Int, - val documentation: String, - val options: Options -) { - internal fun toElement() = - EnumConstantElement(location, name, tag, documentation, options.toElements()) - - internal fun linkOptions(linker: Linker) = options.link(linker) - - internal fun retainAll( - schema: Schema, - markSet: MarkSet - ) = EnumConstant(location, name, tag, documentation, options.retainAll(schema, markSet)) - - fun updateName(name: String) { - this.name = name; - } - - fun updateTag(newTag: Int) { - this.tag = newTag; - - } - - companion object { - internal fun fromElements(elements: List) = - elements.map { - EnumConstant( - it.location, it.name, it.tag, it.documentation, - Options(Options.ENUM_VALUE_OPTIONS, it.options) - ) - } - - internal fun toElements(constants: List) = constants.map { it.toElement() } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumType.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumType.kt deleted file mode 100644 index b3bec6b0..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/EnumType.kt +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.Options.ENUM_OPTIONS -import com.squareup.wire.schema.Reserved.Companion.fromElements -import com.squareup.wire.schema.internal.parser.EnumElement -import java.util.* - -class EnumType public constructor( - private var protoType: ProtoType, - private val location: Location, - private val documentation: String, - private var name: String, - private val constants: List, - private val reserveds: MutableList, - private val options: Options -) : Type() { - private var allowAlias: Any? = null - - // TODO(jrodbx): Konvert to overridden vals, once Type is konverted - override fun location() = location - - override fun type() = protoType - override fun documentation() = documentation - override fun options() = options - override fun nestedTypes() = emptyList() // Enums do not allow nested type declarations. - - fun allowAlias() = allowAlias != null && allowAlias == true - - /** Returns the constant named `name`, or null if this enum has no such constant. */ - fun constant(name: String) = constants.find { it.name == name } - - /** Returns the constant tagged `tag`, or null if this enum has no such constant. */ - fun constant(tag: Int) = constants.find { it.tag == tag } - - fun constants() = constants - - fun reserveds() = reserveds - - internal override fun link(linker: Linker) {} - - internal override fun linkOptions(linker: Linker) { - options.link(linker) - for (constant in constants) { - constant.linkOptions(linker) - } - allowAlias = options.get(ALLOW_ALIAS) - } - - internal override fun validate(linker: Linker) { - var linker = linker - linker = linker.withContext(this) - - if (!allowAlias()) { - validateTagUniqueness(linker) - } - } - - private fun validateTagUniqueness(linker: Linker) { - val tagToConstants = linkedMapOf>() - constants.forEach { - tagToConstants - .getOrPut(it.tag) { mutableListOf() } - .add(it) - } - - for ((tag, constants) in tagToConstants) { - if (constants.size > 1) { - val error = buildString { - append("multiple enum constants share tag $tag:") - constants.forEachIndexed { index, it -> - append("\n ${index + 1}. ${it.name} (${it.location})") - } - } - linker.addError("%s", error) - } - } - } - - fun addReserved(documentation: String?, location: Location?, tag: Int) { - val alreadyReserved = reserveds.stream().anyMatch { reservation: Reserved -> reservation.matchesTag(tag) } - if (!alreadyReserved) { - reserveds.add(Reserved(location!!, documentation!!, Arrays.asList(tag))) - } - } - - fun addReserved(documentation: String?, location: Location?, fieldName: String) { - val alreadyReserved = reserveds.stream().anyMatch { reservation: Reserved -> reservation.matchesName(fieldName) } - if (!alreadyReserved) { - reserveds.add(Reserved(location!!, documentation!!, Arrays.asList(fieldName))) - } - } - - - internal override fun retainAll( - schema: Schema, - markSet: MarkSet - ): Type? { - // If this type is not retained, prune it. - if (!markSet.contains(protoType)) return null - - val retainedConstants = constants - .filter { markSet.contains(ProtoMember.get(protoType, it.name)) } - .map { it.retainAll(schema, markSet) } - - val result = EnumType( - protoType, location, documentation, name, - retainedConstants, reserveds, - options.retainAll(schema, markSet) - ) - result.allowAlias = allowAlias - return result - } - - fun toElement(): EnumElement { - return EnumElement( - location, - name, - documentation, - options.toElements(), - Reserved.toElements(reserveds), - EnumConstant.toElements(constants) - ) - } - - fun name(): String { - return name; - } - - fun updateName(newMessageName: String) { - name = newMessageName; - protoType = ProtoType.get(protoType.enclosingTypeOrPackage(), newMessageName); - } - - companion object { - internal val ALLOW_ALIAS = ProtoMember.get(ENUM_OPTIONS, "allow_alias") - - @JvmStatic - fun fromElement( - protoType: ProtoType, - enumElement: EnumElement - ): EnumType { - val constants = EnumConstant.fromElements(enumElement.constants) - val options = Options(Options.ENUM_OPTIONS, enumElement.options) - val reserveds = fromElements(enumElement.reserveds) - - - - return EnumType( - protoType, enumElement.location, enumElement.documentation, - enumElement.name, constants, reserveds.toMutableList(), options - ) - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extend.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extend.java deleted file mode 100644 index 840b6bec..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extend.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ -package com.squareup.wire.schema; - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.ExtendElement; - -public final class Extend { - private final Location location; - private final String documentation; - private final String name; - private final List fields; - private ProtoType protoType; - - public Extend(Location location, String documentation, String name, List fields) { - this.location = location; - this.documentation = documentation; - this.name = name; - this.fields = fields; - } - - static ImmutableList fromElements(String packageName, List extendElements) { - ImmutableList.Builder extendBuilder = new ImmutableList.Builder<>(); - for (ExtendElement extendElement : extendElements) { - extendBuilder.add(new Extend(extendElement.getLocation(), extendElement.getDocumentation(), extendElement.getName(), - Field.fromElements(packageName, extendElement.getFields(), true))); - } - return extendBuilder.build(); - } - - static ImmutableList toElements(List extendList) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (Extend extend : extendList) { - elements.add(new ExtendElement(extend.location, extend.name, extend.documentation, Field.toElements(extend.fields))); - } - return elements.build(); - } - - public Location location() { - return location; - } - - public ProtoType type() { - return protoType; - } - - public String documentation() { - return documentation; - } - - public List fields() { - return fields; - } - - void link(Linker linker) { - linker = linker.withContext(this); - protoType = linker.resolveMessageType(name); - Type type = linker.get(protoType); - if (type != null) { - ((MessageType) type).addExtensionFields(fields); - } - } - - void validate(Linker linker) { - linker = linker.withContext(this); - linker.validateImport(location(), type()); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extensions.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extensions.kt deleted file mode 100644 index 9bf4c7aa..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Extensions.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.parser.ExtensionsElement - -internal class Extensions private constructor( - val location: Location, - val documentation: String, - val start: Int, - val end: Int -) { - fun validate(linker: Linker) { - if (!Util.isValidTag(start) || !Util.isValidTag(end)) { - linker.withContext(this) - .addError("tags are out of range: %s to %s", start, end) - } - } - - companion object { - @JvmStatic - fun fromElements(elements: List) = - elements.map { Extensions(it.location, it.documentation, it.start, it.end) } - - @JvmStatic - fun toElements(extensions: List) = - extensions.map { ExtensionsElement(it.location, it.documentation, it.start, it.end) } - } -} \ No newline at end of file diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Field.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Field.java deleted file mode 100644 index d4500d0f..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Field.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ -package com.squareup.wire.schema; - -import static com.squareup.wire.schema.Options.FIELD_OPTIONS; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.FieldElement; - -public final class Field { - static final ProtoMember DEPRECATED = ProtoMember.get(FIELD_OPTIONS, "deprecated"); - static final ProtoMember PACKED = ProtoMember.get(FIELD_OPTIONS, "packed"); - - private String packageName; - private final Location location; - private Label label; - private String name; - private String documentation; - private int tag; - private final String defaultValue; - private String elementType; - private final boolean extension; - private final Options options; - private ProtoType type; - private Boolean deprecated; - private Boolean packed; - private boolean redacted; - - public boolean isFromAttribute() { - return fromAttribute; - } - - public void setFromAttribute(boolean fromAttribute) { - this.fromAttribute = fromAttribute; - } - - private boolean fromAttribute = false; - - public boolean isFromElement() { - return fromElement; - } - - public void setFromElement(boolean fromElement) { - this.fromElement = fromElement; - } - - private boolean fromElement = true; - - public Field(String packageName, Location location, Label label, String name, String documentation, int tag, String defaultValue, String elementType, - Options options, boolean extension, boolean fromElement) { - this.packageName = packageName; - this.location = location; - this.label = label; - this.name = name; - this.documentation = documentation; - this.tag = tag; - this.defaultValue = defaultValue; - this.elementType = elementType; - this.extension = extension; - this.options = options; - this.fromElement = fromElement; - } - - public Field(String packageName, Location location, Label label, String name, String documentation, int tag, String elementType, Options options, - boolean fromElement) { - this.packageName = packageName; - this.location = location; - this.label = label; - this.name = name; - this.documentation = documentation; - this.tag = tag; - this.defaultValue = null; - this.elementType = elementType; - this.extension = false; - this.options = options; - this.fromElement = fromElement; - } - - static List fromElements(String packageName, List fieldElements, boolean extension) { - List fields = new ArrayList<>(); - for (FieldElement field : fieldElements) { - fields.add(new Field(packageName, field.getLocation(), field.getLabel(), field.getName(), field.getDocumentation(), field.getTag(), - field.getDefaultValue(), field.getType(), new Options(Options.FIELD_OPTIONS, field.getOptions()), extension, false)); - - } - return fields; - } - - static ImmutableList toElements(List fields) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (Field field : fields) { - elements.add(new FieldElement(field.location, field.label, field.elementType, field.name, field.defaultValue, field.tag, field.documentation, - field.options.toElements())); - } - return elements.build(); - } - - public Location location() { - return location; - } - - public String packageName() { - return packageName; - } - - public void clearPackageName() { - this.packageName = null; - } - - public Label label() { - return label; - } - - public boolean isRepeated() { - return label() == Label.REPEATED; - } - - public boolean isOptional() { - return label() == Label.OPTIONAL; - } - - public boolean isRequired() { - return label() == Label.REQUIRED; - } - - public ProtoType type() { - return type; - } - - public String name() { - return name; - } - - /** - * Returns this field's name, prefixed with its package name. Uniquely identifies extension fields, such as in options. - */ - public String qualifiedName() { - return packageName != null ? packageName + '.' + name : name; - } - - public int tag() { - return tag; - } - - public String documentation() { - return documentation; - } - - public Options options() { - return options; - } - - public boolean isDeprecated() { - return deprecated != null && deprecated; - } - - public boolean isPacked() { - return packed != null && packed; - } - - public boolean isRedacted() { - return redacted; - } - - public String getDefault() { - return defaultValue; - } - - private boolean isPackable(Linker linker, ProtoType type) { - return !type.equals(ProtoType.STRING) && !type.equals(ProtoType.BYTES) && !(linker.get(type) instanceof MessageType); - } - - public boolean isExtension() { - return extension; - } - - void link(Linker linker) { - linker = linker.withContext(this); - type = linker.resolveType(elementType); - } - - void linkOptions(Linker linker) { - linker = linker.withContext(this); - options.link(linker); - deprecated = (Boolean) options().get(DEPRECATED); - packed = (Boolean) options().get(PACKED); - // We allow any package name to be used as long as it ends with '.redacted'. - redacted = options().optionMatches(".*\\.redacted", "true"); - } - - void validate(Linker linker) { - linker = linker.withContext(this); - if (isPacked() && !isPackable(linker, type)) { - linker.addError("packed=true not permitted on %s", type); - } - if (extension && isRequired()) { - linker.addError("extension fields cannot be required", type); - } - linker.validateImport(location(), type); - } - - Field retainAll(Schema schema, MarkSet markSet) { - // For map types only the value can participate in pruning as the key will always be scalar. - if (type.isMap() && !markSet.contains(type.valueType())) { - return null; - } - - if (!markSet.contains(type)) { - return null; - } - - Field result = new Field(packageName, location, label, name, documentation, tag, defaultValue, elementType, options.retainAll(schema, markSet), - extension, fromElement); - result.type = type; - result.deprecated = deprecated; - result.packed = packed; - result.redacted = redacted; - return result; - } - - static ImmutableList retainAll(Schema schema, MarkSet markSet, ProtoType enclosingType, Collection fields) { - ImmutableList.Builder result = ImmutableList.builder(); - for (Field field : fields) { - Field retainedField = field.retainAll(schema, markSet); - if (retainedField != null && markSet.contains(ProtoMember.get(enclosingType, field.name()))) { - result.add(retainedField); - } - } - return result.build(); - } - - @Override - public String toString() { - return "Field [packageName=" + packageName + ", name=" + name + ", tag=" + tag + ", elementType=" + elementType + "]"; - } - - public void setLabel(Label label) { - this.label = label; - } - - public void updateTag(int updatedTag) { - tag = updatedTag; - } - - public enum Label { - OPTIONAL, - REQUIRED, - REPEATED, - /** Indicates the field is a member of a {@code oneof} block. */ - ONE_OF - } - - public void updateElementType(String newFieldType) { - elementType = newFieldType; - } - - public void updatePackageName(String newPackageName) { - packageName = newPackageName; - } - - public void updateName(String newFieldName) { - name = newFieldName; - } - - public void updateDocumentation(String newDocumentation) { - documentation = newDocumentation; - } - - public String getElementType() { - return elementType; - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/IdentifierSet.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/IdentifierSet.java deleted file mode 100644 index 13a17dab..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/IdentifierSet.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.LinkedHashSet; -import java.util.Set; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; - -/** - * A heterogeneous set of rules to include and exclude types and members. If a member is included in the set, its type is implicitly also included. A type that - * is included without a specific member implicitly includes all of that type's members, but not its nested types. - * - *

- * Rules in this set may be in the following forms: - *

    - *
  • Package names, followed by {@code .*}, like {@code squareup.protos.person.*}. This matches types and services defined in the package and its descendant - * packages. - *
  • Fully qualified type and service names, like {@code squareup.protos.person.Person}. - *
  • Fully qualified member names, which are type names followed by a '#', followed by a member name, like {@code squareup.protos.person.Person#address}. - * Members may be fields, enum constants or RPCs. - *
- * - *

- * An identifier set populated with {@code Movie} and {@code Actor#name} contains all members of {@code Movie} (such as {@code Movie#name} and - * {@code Movie#release_date}). It contains the type {@code Actor} and one member {@code Actor#name}, but not {@code Actor#birth_date} or {@code - * Actor#oscar_count}. - * - *

- * This set has included identifiers and excluded identifiers, with excludes taking precedence over includes. That is, if a type {@code Movie} is - * in both the includes and the excludes, it is not contained in the set. - * - *

- * If the includes set is empty, that implies that all elements should be included. Use this to exclude unwanted types and members without also including - * everything else. - * - *

- * Despite the builder, instances of this class are not safe for concurrent use. - */ -public final class IdentifierSet { - private final ImmutableSet includes; - private final ImmutableSet excludes; - private final Set usedIncludes = new LinkedHashSet<>(); - private final Set usedExcludes = new LinkedHashSet<>(); - - private IdentifierSet(Builder builder) { - this.includes = builder.includes.build(); - this.excludes = builder.excludes.build(); - } - - public boolean isEmpty() { - return includes.isEmpty() && excludes.isEmpty(); - } - - /** Returns true if {@code type} is a root. */ - public boolean includes(ProtoType type) { - return includes(type.toString()); - } - - /** Returns true if {@code protoMember} is a root. */ - public boolean includes(ProtoMember protoMember) { - return includes(protoMember.toString()); - } - - /** - * Returns true if {@code identifier} or any of its enclosing identifiers is included. If any enclosing identifier is excluded, that takes precedence and - * this returns false. - */ - private boolean includes(String identifier) { - if (includes.isEmpty()) - return !exclude(identifier); - - String includeMatch = null; - String excludeMatch = null; - for (String rule = identifier; rule != null; rule = enclosing(rule)) { - if (excludes.contains(rule)) { - excludeMatch = rule; - } - if (includes.contains(rule)) { - includeMatch = rule; - } - } - if (excludeMatch != null) { - usedExcludes.add(excludeMatch); - return false; - } - if (includeMatch != null) { - usedIncludes.add(includeMatch); - return true; - } - return false; - } - - /** - * Returns true if {@code type} should be excluded, even if it is a transitive dependency of a root. In that case, the referring member is also excluded. - */ - public boolean excludes(ProtoType type) { - return exclude(type.toString()); - } - - /** Returns true if {@code protoMember} should be excluded. */ - public boolean excludes(ProtoMember protoMember) { - return exclude(protoMember.toString()); - } - - /** Returns true if {@code identifier} or any of its enclosing identifiers is excluded. */ - private boolean exclude(String identifier) { - String excludeMatch = null; - for (String rule = identifier; rule != null; rule = enclosing(rule)) { - if (excludes.contains(rule)) { - excludeMatch = rule; - } - } - if (excludeMatch != null) { - usedExcludes.add(excludeMatch); - return true; - } - return false; - } - - /** - * Returns the identifier or wildcard that encloses {@code identifier}, or null if it is not enclosed. - * - *

    - *
  • If {@code identifier} is a member this returns the enclosing type. - *
  • If it is a type it returns the enclosing package with a wildcard, like {@code - * squareup.dinosaurs.*}. - *
  • If it is a package with a wildcard, it returns the parent package with a wildcard, like {@code squareup.*}. The root wildcard is a lone asterisk, - * {@code *}. - *
- */ - static String enclosing(String identifier) { - int hash = identifier.lastIndexOf('#'); - if (hash != -1) - return identifier.substring(0, hash); - - int from = identifier.endsWith(".*") ? identifier.length() - 3 : identifier.length() - 1; - int dot = identifier.lastIndexOf('.', from); - if (dot != -1) - return identifier.substring(0, dot) + ".*"; - - return !identifier.equals("*") ? "*" : null; - } - - public Set unusedIncludes() { - return Sets.difference(includes, usedIncludes); - } - - public Set unusedExcludes() { - return Sets.difference(excludes, usedExcludes); - } - - public Set includes() { - return includes; - } - - public Set excludes() { - return excludes; - } - - public static final class Builder { - final ImmutableSet.Builder includes = ImmutableSet.builder(); - final ImmutableSet.Builder excludes = ImmutableSet.builder(); - - public Builder include(String identifier) { - if (identifier == null) - throw new NullPointerException("identifier == null"); - includes.add(identifier); - return this; - } - - public Builder include(Iterable identifiers) { - if (identifiers == null) - throw new NullPointerException("identifiers == null"); - includes.addAll(identifiers); - return this; - } - - public Builder exclude(String identifier) { - if (identifier == null) - throw new NullPointerException("identifier == null"); - excludes.add(identifier); - return this; - } - - public Builder exclude(Iterable identifiers) { - if (identifiers == null) - throw new NullPointerException("identifiers == null"); - excludes.addAll(identifiers); - return this; - } - - public IdentifierSet build() { - return new IdentifierSet(this); - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Linker.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Linker.java deleted file mode 100644 index be22d92a..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Linker.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.*; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.squareup.wire.schema.internal.Util; - -/** Links local field types and option types to the corresponding declarations. */ -public final class Linker { - private final ImmutableList protoFiles; - private final Map protoTypeNames; - private final Multimap imports; - private final List errors; - private final List contextStack; - - public Linker(Iterable protoFiles) { - this.protoFiles = ImmutableList.copyOf(protoFiles); - this.protoTypeNames = new LinkedHashMap<>(); - this.imports = LinkedHashMultimap.create(); - this.contextStack = Collections.emptyList(); - this.errors = new ArrayList<>(); - } - - private Linker(Linker enclosing, Object additionalContext) { - this.protoFiles = enclosing.protoFiles; - this.protoTypeNames = enclosing.protoTypeNames; - this.imports = enclosing.imports; - this.contextStack = Util.concatenate(enclosing.contextStack, additionalContext); - this.errors = enclosing.errors; - } - - public Schema link() { - // Register the types. - for (ProtoFile protoFile : protoFiles) { - for (Type type : protoFile.types()) { - register(type); - } - } - - // Link extensions. This depends on type registration. - for (ProtoFile protoFile : protoFiles) { - Linker linker = withContext(protoFile); - for (Extend extend : protoFile.extendList()) { - extend.link(linker); - } - } - - // Link proto types and services. - for (ProtoFile protoFile : protoFiles) { - Linker linker = withContext(protoFile); - for (Type type : protoFile.types()) { - type.link(linker); - } - for (Service service : protoFile.services()) { - service.link(linker); - } - } - - // Link options. We can't link any options until we've linked all fields! - for (ProtoFile protoFile : protoFiles) { - Linker linker = withContext(protoFile); - protoFile.linkOptions(linker); - for (Type type : protoFile.types()) { - type.linkOptions(linker); - } - for (Service service : protoFile.services()) { - service.linkOptions(linker); - } - } - - // Compute public imports so we know that importing a.proto also imports b.proto and c.proto. - Multimap publicImports = LinkedHashMultimap.create(); - for (ProtoFile protoFile : protoFiles) { - publicImports.putAll(protoFile.location().getPath(), protoFile.publicImports()); - } - // For each proto, gather its imports and its transitive imports. - for (ProtoFile protoFile : protoFiles) { - Collection sink = imports.get(protoFile.location().getPath()); - addImports(sink, protoFile.imports(), publicImports); - addImports(sink, protoFile.publicImports(), publicImports); - } - - // Validate the linked schema. - for (ProtoFile protoFile : protoFiles) { - Linker linker = withContext(protoFile); - protoFile.validate(linker); - - for (Type type : protoFile.types()) { - type.validate(linker); - } - for (Service service : protoFile.services()) { - service.validate(linker); - } - for (Extend extend : protoFile.extendList()) { - extend.validate(linker); - } - } - - if (!errors.isEmpty()) { - throw new SchemaException(errors); - } - - return new Schema(protoFiles); - } - - /** Add all paths in {@code paths} to {@code sink}, plus their public imports, recursively. */ - private void addImports(Collection sink, Collection paths, Multimap publicImports) { - for (String path : paths) { - if (sink.add(path)) { - addImports(sink, publicImports.get(path), publicImports); - } - } - } - - private void register(Type type) { - protoTypeNames.put(type.type().toString(), type); - for (Type nestedType : type.nestedTypes()) { - register(nestedType); - } - } - - /** Returns the type name for the scalar, relative or fully-qualified name {@code name}. */ - ProtoType resolveType(String name) { - return resolveType(name, false); - } - - /** Returns the type name for the relative or fully-qualified name {@code name}. */ - ProtoType resolveMessageType(String name) { - return resolveType(name, true); - } - - private ProtoType resolveType(String name, boolean messageOnly) { - ProtoType type = ProtoType.get(name); - if (type.isScalar()) { - if (messageOnly) { - addError("expected a message but was %s", name); - } - return type; - } - - if (type.isMap()) { - if (messageOnly) { - addError("expected a message but was %s", name); - } - ProtoType keyType = resolveType(type.keyType().toString(), false); - ProtoType valueType = resolveType(type.valueType().toString(), false); - return new ProtoType(keyType, valueType, name); - } - - Type resolved = resolve(name, protoTypeNames); - if (resolved == null) { - addError("unable to resolve %s", name); - return ProtoType.BYTES; // Just return any placeholder. - } - - if (messageOnly && !(resolved instanceof MessageType)) { - addError("expected a message but was %s", name); - return ProtoType.BYTES; // Just return any placeholder. - } - - return resolved.type(); - } - - T resolve(String name, Map map) { - if (name.startsWith(".")) { - // If name starts with a '.', the rest of it is fully qualified. - T result = map.get(name.substring(1)); - if (result != null) - return result; - } else { - // We've got a name suffix, like 'Person' or 'protos.Person'. Start the search from with the - // longest prefix like foo.bar.Baz.Quux, shortening the prefix until we find a match. - String prefix = resolveContext(); - while (!prefix.isEmpty()) { - T result = map.get(prefix + '.' + name); - if (result != null) - return result; - - // Strip the last nested class name or package name from the end and try again. - int dot = prefix.lastIndexOf('.'); - prefix = dot != -1 ? prefix.substring(0, dot) : ""; - } - T result = map.get(name); - if (result != null) - return result; - } - return null; - } - - private String resolveContext() { - for (int i = contextStack.size() - 1; i >= 0; i--) { - Object context = contextStack.get(i); - if (context instanceof Type) { - return ((Type) context).type().toString(); - } else if (context instanceof ProtoFile) { - String packageName = ((ProtoFile) context).packageName(); - return packageName != null ? packageName : ""; - } else if (context instanceof Field && ((Field) context).isExtension()) { - String packageName = ((Field) context).packageName(); - return packageName != null ? packageName : ""; - } - } - throw new IllegalStateException(); - } - - /** Returns the current package name from the context stack. */ - String packageName() { - for (Object context : contextStack) { - if (context instanceof ProtoFile) - return ((ProtoFile) context).packageName(); - } - return null; - } - - /** Returns the type or null if it doesn't exist. */ - public Type get(ProtoType protoType) { - return protoTypeNames.get(protoType.toString()); - } - - /** Returns the field named {@code field} on the message type of {@code self}. */ - Field dereference(Field self, String field) { - if (field.startsWith("[") && field.endsWith("]")) { - field = field.substring(1, field.length() - 1); - } - - Type type = protoTypeNames.get(self.type().toString()); - if (type instanceof MessageType) { - MessageType messageType = (MessageType) type; - - Field messageField = messageType.field(field); - if (messageField != null) - return messageField; - - Map typeExtensions = messageType.extensionFieldsMap(); - Field extensionField = resolve(field, typeExtensions); - if (extensionField != null) - return extensionField; - - String finalField = field; - if (messageType.oneOfs().stream().anyMatch(o -> o.name().equals(finalField))) { - OneOf oneOf = messageType.oneOfs().stream().filter(o -> o.name().equals(finalField)).findFirst().get(); - Field oneofField = oneOf.fields().get(0); - oneofField.updateName(self.name() + "." + oneOf.name() + "." + oneofField.name()); - return oneofField; - } - } - // TODO oneof - - return null; // Unable to traverse this field path. - } - - /** Validate that the tags of {@code fields} are unique and in range. */ - void validateFields(Iterable fields, List reserveds) { - Multimap tagToField = LinkedHashMultimap.create(); - Multimap nameToField = LinkedHashMultimap.create(); - for (Field field : fields) { - int tag = field.tag(); - if (!Util.isValidTag(tag)) { - withContext(field).addError("tag is out of range: %s", tag); - } - - for (Reserved reserved : reserveds) { - if (reserved.matchesTag(tag)) { - withContext(field).addError("tag %s is reserved (%s)", tag, reserved.getLocation()); - } - if (reserved.matchesName(field.name())) { - withContext(field).addError("name '%s' is reserved (%s)", field.name(), reserved.getLocation()); - } - } - - tagToField.put(tag, field); - nameToField.put(field.qualifiedName(), field); - } - - for (Map.Entry> entry : tagToField.asMap().entrySet()) { - if (entry.getValue().size() > 1) { - StringBuilder error = new StringBuilder(); - error.append(String.format("multiple fields share tag %s:", entry.getKey())); - int index = 1; - for (Field field : entry.getValue()) { - error.append(String.format("\n %s. %s (%s)", index++, field.name(), field.location())); - } - addError("%s", error); - } - } - - for (Collection collidingFields : nameToField.asMap().values()) { - if (collidingFields.size() > 1) { - Field first = collidingFields.iterator().next(); - StringBuilder error = new StringBuilder(); - error.append(String.format("multiple fields share name %s:", first.name())); - int index = 1; - for (Field field : collidingFields) { - error.append(String.format("\n %s. %s (%s)", index++, field.name(), field.location())); - } - addError("%s", error); - } - } - } - - void validateEnumConstantNameUniqueness(Iterable nestedTypes) { - Multimap nameToType = LinkedHashMultimap.create(); - for (Type type : nestedTypes) { - if (type instanceof EnumType) { - EnumType enumType = (EnumType) type; - for (EnumConstant enumConstant : enumType.constants()) { - nameToType.put(enumConstant.getName(), enumType); - } - } - } - - for (Map.Entry> entry : nameToType.asMap().entrySet()) { - if (entry.getValue().size() > 1) { - StringBuilder error = new StringBuilder(); - String constant = entry.getKey(); - int index = 1; - error.append(String.format("multiple enums share constant %s:", constant)); - for (EnumType enumType : entry.getValue()) { - error.append(String.format("\n %s. %s.%s (%s)", index++, enumType.type(), constant, enumType.constant(constant).getLocation())); - } - addError("%s", error); - } - } - } - - void validateImport(Location location, ProtoType type) { - // Map key type is always scalar. No need to validate it. - if (type.isMap()) - type = type.valueType(); - - if (type.isScalar()) - return; - - String path = location.getPath(); - String requiredImport = get(type).location().getPath(); - if (!path.equals(requiredImport) && !imports.containsEntry(path, requiredImport)) { - addError("%s needs to import %s", path, requiredImport); - } - } - - /** Returns a new linker that uses {@code context} to resolve type names and report errors. */ - Linker withContext(Object context) { - return new Linker(this, context); - } - - void addError(String format, Object... args) { - StringBuilder error = new StringBuilder(); - error.append(String.format(format, args)); - - for (int i = contextStack.size() - 1; i >= 0; i--) { - Object context = contextStack.get(i); - String prefix = (i == contextStack.size() - 1) ? "\n for" : "\n in"; - - if (context instanceof Rpc) { - Rpc rpc = (Rpc) context; - error.append(String.format("%s rpc %s (%s)", prefix, rpc.name(), rpc.location())); - - } else if (context instanceof Extend) { - Extend extend = (Extend) context; - ProtoType type = extend.type(); - error.append(type != null ? String.format("%s extend %s (%s)", prefix, type, extend.location()) - : String.format("%s extend (%s)", prefix, extend.location())); - - } else if (context instanceof Field) { - Field field = (Field) context; - error.append(String.format("%s field %s (%s)", prefix, field.name(), field.location())); - - } else if (context instanceof MessageType) { - MessageType message = (MessageType) context; - error.append(String.format("%s message %s (%s)", prefix, message.type(), message.location())); - - } else if (context instanceof EnumType) { - EnumType enumType = (EnumType) context; - error.append(String.format("%s enum %s (%s)", prefix, enumType.type(), enumType.location())); - - } else if (context instanceof Service) { - Service service = (Service) context; - error.append(String.format("%s service %s (%s)", prefix, service.type(), service.location())); - - } else if (context instanceof Extensions) { - Extensions extensions = (Extensions) context; - error.append(String.format("%s extensions (%s)", prefix, extensions.getLocation())); - } - } - - errors.add(error.toString()); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Location.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Location.kt deleted file mode 100644 index 527bad51..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Location.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.google.common.base.CharMatcher -import java.io.File - -/** - * Locates a .proto file, or a position within a .proto file, on the file system. This includes a - * base directory or a .jar file, and a path relative to that base. - */ -data class Location( - /** The base of this location; typically a directory or .jar file. */ - val base: String, - - /** The path to this location relative to [.base]. */ - val path: String, - - /** The line number of this location, or -1 for no specific line number. */ - val line: Int = -1, - - /** The column on the line of this location, or -1 for no specific column. */ - val column: Int = -1 -) { - - fun at(line: Int, column: Int) = Location(base, path, line, column) - - /** Returns a copy of this location with an empty base. */ - fun withoutBase() = Location("", path, line, column) - - /** Returns a copy of this location including only its path. */ - fun withPathOnly() = Location("", path, -1, -1) - - override fun toString() = buildString { - if (!base.isEmpty()) { - append(base) - append(File.separator) - } - append(path) - if (line != -1) { - append(" at $line") - if (column != -1) { - append(":$column") - } - } - } - - companion object { - @JvmStatic - fun get(path: String): Location { - return get("", path) - } - - @JvmStatic - fun get( - base: String, - path: String - ): Location { - val newBase = CharMatcher.`is`('/') - .trimTrailingFrom(base) - return Location(newBase, path, -1, -1) - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/MarkSet.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/MarkSet.java deleted file mode 100644 index fde8347f..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/MarkSet.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static com.google.common.base.Preconditions.checkArgument; - -import java.util.LinkedHashSet; -import java.util.Set; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; - -/** - * A mark set is used in three phases: - * - *
    - *
  1. Marking root types and root members. These are the identifiers specifically identified by the user in the includes set. In this phase it is an error to - * mark a type that is excluded, or to both a type and one of its members. - *
  2. Marking members transitively reachable by those roots. In this phase if a member is visited, the member's enclosing type is marked instead, unless it is - * of a type that has a specific member already marked. - *
  3. Retaining which members and types have been marked. - *
- */ -final class MarkSet { - final IdentifierSet identifierSet; - final Set types = new LinkedHashSet<>(); - final Multimap members = LinkedHashMultimap.create(); - - MarkSet(IdentifierSet identifierSet) { - this.identifierSet = identifierSet; - } - - /** - * Marks {@code protoMember}, throwing if it is explicitly excluded, or if its enclosing type is also specifically included. This implicitly excludes other - * members of the same type. - */ - void root(ProtoMember protoMember) { - if (protoMember == null) - throw new NullPointerException("protoMember == null"); - checkArgument(!identifierSet.excludes(protoMember)); - checkArgument(!types.contains(protoMember.type())); - members.put(protoMember.type(), protoMember); - } - - /** - * Marks {@code type}, throwing if it is explicitly excluded, or if any of its members are also specifically included. - */ - void root(ProtoType type) { - if (type == null) - throw new NullPointerException("type == null"); - checkArgument(!identifierSet.excludes(type)); - checkArgument(!members.containsKey(type)); - types.add(type); - } - - /** - * Marks a type as transitively reachable by the includes set. Returns true if the mark is new, the type will be retained, and its own dependencies should - * be traversed. - */ - boolean mark(ProtoType type) { - if (type == null) - throw new NullPointerException("type == null"); - if (identifierSet.excludes(type)) - return false; - return types.add(type); - } - - /** - * Marks a member as transitively reachable by the includes set. Returns true if the mark is new, the member will be retained, and its own dependencies - * should be traversed. - */ - boolean mark(ProtoMember protoMember) { - if (protoMember == null) - throw new NullPointerException("type == null"); - if (identifierSet.excludes(protoMember)) - return false; - return members.containsKey(protoMember.type()) ? members.put(protoMember.type(), protoMember) : types.add(protoMember.type()); - } - - /** Returns true if all members of {@code type} are marked and should be retained. */ - boolean containsAllMembers(ProtoType type) { - if (type == null) - throw new NullPointerException("type == null"); - return types.contains(type) && !members.containsKey(type); - } - - /** Returns true if {@code type} is marked and should be retained. */ - boolean contains(ProtoType type) { - if (type == null) - throw new NullPointerException("type == null"); - return types.contains(type); - } - - /** Returns true if {@code member} is marked and should be retained. */ - boolean contains(ProtoMember protoMember) { - if (protoMember == null) - throw new NullPointerException("protoMember == null"); - if (identifierSet.excludes(protoMember)) - return false; - return members.containsKey(protoMember.type()) ? members.containsEntry(protoMember.type(), protoMember) : types.contains(protoMember.type()); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/MessageType.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/MessageType.java deleted file mode 100644 index def001d4..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/MessageType.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 - 2020 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.GroupElement; -import com.squareup.wire.schema.internal.parser.MessageElement; -import com.squareup.wire.schema.internal.parser.TypeElement; - -public final class MessageType extends Type { - public static final String XSD_MESSAGE_OPTIONS_PACKAGE = "xsd"; - public static final String BASE_TYPE_MESSAGE_OPTION = "base_type"; - public static final String XSD_BASE_TYPE_MESSAGE_OPTION_NAME = XSD_MESSAGE_OPTIONS_PACKAGE + "." + BASE_TYPE_MESSAGE_OPTION; - public static final int FIELD_NUM_INCREMENT = 10; - private ProtoType protoType; - private final Location location; - private String documentation; - private String name; - private List declaredFields; - private final List extensionFields; - private final List oneOfs; - private final List nestedTypes; - private final List extensionsList; - private final List reserveds; - private final Options options; - private int fieldNum = 0; - - public boolean isWrapperMessageType() { - return wrapperMessageType; - } - - public void setWrapperMessageType(boolean wrapperMessageType) { - this.wrapperMessageType = wrapperMessageType; - } - - private boolean wrapperMessageType = false; - - public MessageType(ProtoType protoType, Location location, String documentation, String name, List declaredFields, List extensionFields, - List oneOfs, List nestedTypes, List extensionsList, List reserveds, Options options) { - this.protoType = protoType; - this.location = location; - this.documentation = documentation; - this.name = name; - this.declaredFields = declaredFields; - this.extensionFields = extensionFields; - this.oneOfs = oneOfs; - this.nestedTypes = nestedTypes; - this.extensionsList = extensionsList; - this.reserveds = reserveds; - this.options = checkNotNull(options); - } - - public MessageType(ProtoType protoType, Location location, String documentation, String name, Options options) { - this.protoType = protoType; - this.location = location; - this.documentation = documentation; - this.name = name; - - // Empty values - this.options = checkNotNull(options); - this.declaredFields = new ArrayList<>(); - this.extensionFields = new ArrayList<>(); - this.extensionsList = new ArrayList<>(); - this.oneOfs = new ArrayList<>(); - this.nestedTypes = new ArrayList<>(); - this.reserveds = new ArrayList<>(); - } - - public List getReserveds() { - return reserveds; - } - - public void addReserved(String documentation, Location location, int tag) { - boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesTag(tag)); - if (!alreadyReserved) { - reserveds.add(new Reserved(location, documentation, Arrays.asList(tag))); - } - } - - public void addReserved(String documentation, Location location, String fieldName) { - boolean alreadyReserved = reserveds.stream().anyMatch(reservation -> reservation.matchesName(fieldName)); - if (!alreadyReserved) { - reserveds.add(new Reserved(location, documentation, Arrays.asList(fieldName))); - } - } - - @Override - public String toString() { - return "MessageType [name=" + name + "]"; - } - - public int getNextFieldNum() { - fieldNum++; - return fieldNum; - } - - public void advanceFieldNum() { - - if (fieldNum == 0) { - return; // No need to advance num - } - - int newFieldNum = (fieldNum + FIELD_NUM_INCREMENT) - (fieldNum % FIELD_NUM_INCREMENT); - if (newFieldNum - (FIELD_NUM_INCREMENT / 3) < fieldNum) { - // Ensure at least 30% field nums available for later - fieldNum = newFieldNum; - advanceFieldNum(); - } else { - fieldNum = newFieldNum; - - } - - } - - public String getName() { - return name; - } - - public void updateName(String newName) { - this.name = newName; - protoType = ProtoType.get(protoType.enclosingTypeOrPackage(), newName); - - } - - @Override - public Location location() { - return location; - } - - @Override - public ProtoType type() { - return protoType; - } - - @Override - public String documentation() { - return documentation; - } - - @Override - public List nestedTypes() { - return nestedTypes; - } - - @Override - public Options options() { - return options; - } - - public void addField(Field f) { - declaredFields.add(f); - } - - public void setDeclaredFields(List newFields) { - this.declaredFields = newFields; - } - - public List fields() { - List allFields = new ArrayList<>(declaredFields); - allFields.addAll(extensionFields); - - return allFields; - } - - public void removeDeclaredField(Field f) { - declaredFields.remove(f); - } - - public ImmutableList fieldsAndOneOfFields() { - ImmutableList.Builder result = ImmutableList.builder(); - result.addAll(declaredFields); - result.addAll(extensionFields); - for (OneOf oneOf : oneOfs) { - result.addAll(oneOf.fields()); - } - return result.build(); - } - - /** - * Returns the field named {@code name}, or null if this type has no such field. - */ - public Field field(String name) { - - for (Field field : declaredFields) { - if (field.name().equals(name)) { - return field; - } - } - for (OneOf oneOf : oneOfs) { - for (Field field : oneOf.fields()) { - if (field.name().equals(name)) { - return field; - } - } - } - return null; - } - - /** - * Returns the field with the qualified name {@code qualifiedName}, or null if this type has no such field. - */ - public Field extensionField(String qualifiedName) { - for (Field field : extensionFields) { - if (field.qualifiedName().equals(qualifiedName)) { - return field; - } - } - return null; - } - - /** - * Returns the field tagged {@code tag}, or null if this type has no such field. - */ - public Field field(int tag) { - for (Field field : declaredFields) { - if (field.tag() == tag) { - return field; - } - } - for (Field field : extensionFields) { - if (field.tag() == tag) { - return field; - } - } - return null; - } - - public List oneOfs() { - return oneOfs; - } - - public List extensions() { - return extensionsList; - } - - Map extensionFieldsMap() { - // TODO(jwilson): simplify this to just resolve field values directly. - Map extensionsForType = new LinkedHashMap<>(); - for (Field field : extensionFields) { - extensionsForType.put(field.qualifiedName(), field); - } - return extensionsForType; - } - - void addExtensionFields(List fields) { - extensionFields.addAll(fields); - } - - void link(Linker linker) { - linker = linker.withContext(this); - for (Field field : declaredFields) { - field.link(linker); - } - for (Field field : extensionFields) { - field.link(linker); - } - for (OneOf oneOf : oneOfs) { - oneOf.link(linker); - } - for (Type type : nestedTypes) { - type.link(linker); - } - } - - void linkOptions(Linker linker) { - linker = linker.withContext(this); - for (Type type : nestedTypes) { - type.linkOptions(linker); - } - for (Field field : declaredFields) { - field.linkOptions(linker); - } - for (Field field : extensionFields) { - field.linkOptions(linker); - } - for (OneOf oneOf : oneOfs) { - oneOf.linkOptions(linker); - } - options.link(linker); - } - - void validate(Linker linker) { - linker = linker.withContext(this); - linker.validateFields(fieldsAndOneOfFields(), reserveds); - linker.validateEnumConstantNameUniqueness(nestedTypes); - for (Field field : fieldsAndOneOfFields()) { - field.validate(linker); - } - for (Type type : nestedTypes) { - type.validate(linker); - } - for (Extensions extensions : extensionsList) { - extensions.validate(linker); - } - } - - @Override - Type retainAll(Schema schema, MarkSet markSet) { - ImmutableList.Builder retainedNestedTypesBuilder = ImmutableList.builder(); - for (Type nestedType : nestedTypes) { - Type retainedNestedType = nestedType.retainAll(schema, markSet); - if (retainedNestedType != null) { - retainedNestedTypesBuilder.add(retainedNestedType); - } - } - - ImmutableList retainedNestedTypes = retainedNestedTypesBuilder.build(); - if (!markSet.contains(protoType)) { - // If this type is not retained, and none of its nested types are retained, prune it. - if (retainedNestedTypes.isEmpty()) { - return null; - } - // If this type is not retained but retained nested types, replace it with an enclosing type. - return new EnclosingType(location, protoType, documentation, retainedNestedTypes); - } - - ImmutableList.Builder retainedOneOfsBuilder = ImmutableList.builder(); - for (OneOf oneOf : oneOfs) { - OneOf retainedOneOf = oneOf.retainAll(schema, markSet, protoType); - if (retainedOneOf != null) { - retainedOneOfsBuilder.add(retainedOneOf); - } - } - ImmutableList retainedOneOfs = retainedOneOfsBuilder.build(); - - return new MessageType(protoType, location, documentation, name, Field.retainAll(schema, markSet, protoType, declaredFields), - Field.retainAll(schema, markSet, protoType, extensionFields), retainedOneOfs, retainedNestedTypes, extensionsList, reserveds, - options.retainAll(schema, markSet)); - } - - static MessageType fromElement(String packageName, ProtoType protoType, MessageElement messageElement) { - if (!messageElement.getGroups().isEmpty()) { - GroupElement group = messageElement.getGroups().get(0); - throw new IllegalStateException(group.getLocation() + ": 'group' is not supported"); - } - - List declaredFields = Field.fromElements(packageName, messageElement.getFields(), false); - - // Extension fields be populated during linking. - List extensionFields = new ArrayList<>(); - - ImmutableList oneOfs = OneOf.fromElements(packageName, messageElement.getOneOfs(), false); - - ImmutableList.Builder nestedTypes = ImmutableList.builder(); - for (TypeElement nestedType : messageElement.getNestedTypes()) { - nestedTypes.add(Type.get(packageName, protoType.nestedType(nestedType.getName()), nestedType)); - } - - List extensionsList = Extensions.fromElements(messageElement.getExtensions()); - - List reserveds = Reserved.fromElements(messageElement.getReserveds()); - - Options options = new Options(Options.MESSAGE_OPTIONS, messageElement.getOptions()); - - return new MessageType(protoType, messageElement.getLocation(), messageElement.getDocumentation(), messageElement.getName(), declaredFields, - extensionFields, oneOfs, nestedTypes.build(), extensionsList, reserveds, options); - } - - MessageElement toElement() { - return new MessageElement(location, name, documentation, Type.toElements(nestedTypes), options.toElements(), Reserved.toElements(reserveds), - Field.toElements(declaredFields), OneOf.toElements(oneOfs), Extensions.toElements(extensionsList), Collections.emptyList() // groups - ); - } - - public void updateDocumentation(String documentation) { - this.documentation = documentation; - } - - public void removeOneOf(OneOf oneOfToRemove) { - oneOfs.remove(oneOfToRemove); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/OneOf.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/OneOf.java deleted file mode 100644 index ce106300..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/OneOf.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.Collections; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.GroupElement; -import com.squareup.wire.schema.internal.parser.OneOfElement; - -public final class OneOf { - private final String name; - private String documentation; - private final List fields; - private final Options options; - - public OneOf(String name, String documentation, List fields, Options options) { - this.name = name; - this.documentation = documentation; - this.fields = fields; - this.options = options; - } - - public String name() { - return name; - } - - public String documentation() { - return documentation; - } - - public List fields() { - return fields; - } - - public Options options() { - return options; - } - - void link(Linker linker) { - for (Field field : fields) { - field.link(linker); - } - } - - void linkOptions(Linker linker) { - options.link(linker); - for (Field field : fields) { - field.linkOptions(linker); - } - } - - OneOf retainAll(Schema schema, MarkSet markSet, ProtoType enclosingType) { - ImmutableList retainedFields = Field.retainAll(schema, markSet, enclosingType, fields); - if (retainedFields.isEmpty()) - return null; - return new OneOf(name, documentation, retainedFields, options.retainAll(schema, markSet)); - } - - static ImmutableList fromElements(String packageName, List elements, boolean extension) { - ImmutableList.Builder oneOfs = ImmutableList.builder(); - for (OneOfElement oneOf : elements) { - if (!oneOf.getGroups().isEmpty()) { - GroupElement group = oneOf.getGroups().get(0); - throw new IllegalStateException(group.getLocation() + ": 'group' is not supported"); - } - oneOfs.add(new OneOf(oneOf.getName(), oneOf.getDocumentation(), Field.fromElements(packageName, oneOf.getFields(), extension), - new Options(Options.ONE_OF_OPTIONS, oneOf.getOptions()))); - } - return oneOfs.build(); - } - - static ImmutableList toElements(List oneOfs) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (OneOf oneOf : oneOfs) { - elements.add(new OneOfElement(oneOf.name, oneOf.documentation, Field.toElements(oneOf.fields), Collections.emptyList(), // groups - Collections.emptyList())); - } - return elements.build(); - } - - public void updateDocumentation(String documentation) { - this.documentation = documentation; - } - - @Override - public String toString() { - return "OneOf [" + "name='" + name + '\'' + ", documentation='" + documentation + '\'' + ", fields=" + fields + ']'; - } - - public void addField(Field newField) { - fields.add(newField); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Options.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Options.java deleted file mode 100644 index 25653d3b..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Options.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.getOnlyElement; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.squareup.wire.schema.internal.parser.OptionElement; - -/** - * A set of options declared on a message declaration, field declaration, enum declaration, enum constant declaration, service declaration, RPC method - * declaration, or proto file declaration. Options values may be arbitrary protocol buffer messages, but must be valid protocol buffer messages. - */ -public final class Options { - public static final ProtoType FILE_OPTIONS = ProtoType.get("google.protobuf.FileOptions"); - public static final ProtoType MESSAGE_OPTIONS = ProtoType.get("google.protobuf.MessageOptions"); - public static final ProtoType FIELD_OPTIONS = ProtoType.get("google.protobuf.FieldOptions"); - public static final ProtoType ENUM_OPTIONS = ProtoType.get("google.protobuf.EnumOptions"); - public static final ProtoType ENUM_VALUE_OPTIONS = ProtoType.get("google.protobuf.EnumValueOptions"); - public static final ProtoType SERVICE_OPTIONS = ProtoType.get("google.protobuf.ServiceOptions"); - public static final ProtoType METHOD_OPTIONS = ProtoType.get("google.protobuf.MethodOptions"); - public static final ProtoType ONE_OF_OPTIONS = ProtoType.get("google.protobuf.OneofOptions"); - - private final ProtoType optionType; - private final List optionElements; - private Map map; - - public Options(ProtoType optionType, List elements) { - this.optionType = optionType; - this.optionElements = new ArrayList<>(elements); - } - - public void replaceOption(String optionName, OptionElement element) { - Object toBeRemoved = null; - - for (OptionElement e : optionElements) { - if (e.getName().equals(optionName)) { - toBeRemoved = e; - break; - } - } - - if (toBeRemoved != null) { - optionElements.remove(toBeRemoved); - optionElements.add(element); - } - } - - public List getOptionElements() { - return optionElements; - } - - /** - * Returns a map with the values for these options. Map values may be either a single entry, like {@code {deprecated: "true"}}, or more sophisticated, with - * nested maps and lists. - * - *

- * The map keys are always {@link ProtoMember} instances, even for nested maps. The values are always either lists, maps, or strings. - */ - public Map map() { - return map; - } - - public Object get(ProtoMember protoMember) { - checkNotNull(protoMember, "protoMember"); - return map.get(protoMember); - } - - /** - * Returns true if any of the options in {@code options} matches both of the regular expressions provided: its name matches the option's name and its value - * matches the option's value. - */ - public boolean optionMatches(String namePattern, String valuePattern) { - Matcher nameMatcher = Pattern.compile(namePattern).matcher(""); - Matcher valueMatcher = Pattern.compile(valuePattern).matcher(""); - for (Map.Entry entry : map.entrySet()) { - if (nameMatcher.reset(entry.getKey().member()).matches() && valueMatcher.reset(String.valueOf(entry.getValue())).matches()) { - return true; - } - } - return false; - } - - List toElements() { - return optionElements; - } - - void link(Linker linker) { - ImmutableMap map = ImmutableMap.of(); - for (OptionElement option : optionElements) { - Map canonicalOption = canonicalizeOption(linker, optionType, option); - if (canonicalOption != null) { - map = union(linker, map, canonicalOption); - } - } - - this.map = map; - } - - Map canonicalizeOption(Linker linker, ProtoType extensionType, OptionElement option) { - Type type = linker.get(extensionType); - if (!(type instanceof MessageType)) { - return null; // No known extensions for the given extension type. - } - MessageType messageType = (MessageType) type; - - String[] path; - Field field = messageType.field(option.getName()); - if (field != null) { - // This is an option declared by descriptor.proto. - path = new String[] { option.getName() }; - } else { - // This is an option declared by an extension. - Map extensionsForType = messageType.extensionFieldsMap(); - - StringBuilder pathBuilder = new StringBuilder(); - pathBuilder.append(option.getName()); - if (option.getValue() instanceof OptionElement) { - OptionElement optionElement = (OptionElement) option.getValue(); - if (optionElement.getName().contains(".")) { - pathBuilder.append("."); - pathBuilder.append(optionElement.getName()); - } - } - path = resolveFieldPath(pathBuilder.toString(), extensionsForType.keySet()); - String packageName = linker.packageName(); - if (path == null && packageName != null) { - // If the path couldn't be resolved, attempt again by prefixing it with the package name. - path = resolveFieldPath(packageName + "." + option.getName(), extensionsForType.keySet()); - } - if (path == null) { - return null; // Unable to find the root of this field path. - } - - field = extensionsForType.get(path[0]); - } - - Map result = new LinkedHashMap<>(); - Map last = result; - ProtoType lastProtoType = messageType.type(); - int i = 1; - boolean found = false; - while (i < path.length && !found) { - Map nested = new LinkedHashMap<>(); - last.put(ProtoMember.get(lastProtoType, field), nested); - lastProtoType = field.type(); - last = nested; - field = linker.dereference(field, path[i]); - if (field == null) { - return null; // Unable to dereference this path segment. - } - i++; - if (field.name().contains(".") && field.name().endsWith(path[path.length - 1])) { - found = true; - lastProtoType = field.type(); - } - } - - Object value = canonicalizeValue(linker, field, option.getValue()); - if (value != null) { - last.put(ProtoMember.get(lastProtoType, field), value); - } - return result; - } - - /** - * Given a path like {@code a.b.c.d} and a set of paths like {@code {a.b.c, a.f.g, h.j}}, this returns the original path split on dots such that the first - * element is in the set. For the above example it would return the array {@code [a.b.c, d]}. - * - *

- * Typically the input path is a package name like {@code a.b}, followed by a dot and a sequence of field names. The first field name is an extension field; - * subsequent field names make a path within that extension. - * - *

- * Note that a single input may yield multiple possible answers, such as when package names and field names collide. This method prefers shorter package - * names though that is an implementation detail. - */ - static String[] resolveFieldPath(String name, Set fullyQualifiedNames) { - // Try to resolve a local name. - for (int i = 0; i < name.length(); i++) { - i = name.indexOf('.', i); - if (i == -1) { - i = name.length(); - } - - String candidate = name.substring(0, i); - if (fullyQualifiedNames.contains(candidate)) { - String[] path = name.substring(i).split("\\.", -1); - path[0] = name.substring(0, i); - return path; - } - } - - return null; - } - - private Object canonicalizeValue(Linker linker, Field context, Object value) { - if (value instanceof OptionElement) { - ImmutableMap.Builder result = ImmutableMap.builder(); - OptionElement option = (OptionElement) value; - - String optionname = option.getName(); - Field field = linker.dereference(context, optionname); - if (field == null && option.getKind() != OptionElement.Kind.MAP) { - return null; - } else if (field == null && option.getKind() == OptionElement.Kind.MAP) { - Map mapOption = (Map) option.getValue(); - mapOption.entrySet().stream().filter(e -> e.getValue() != null).forEach(e -> { - result.put(ProtoMember.get(context.packageName() + "." + context.getElementType() + "#" + e.getKey()), e.getValue()); - }); - - } - - else { - ProtoMember protoMember = ProtoMember.get(context.type(), field); - Object canonicalizeValue = canonicalizeValue(linker, field, option.getValue()); - if (canonicalizeValue != null) { - result.put(protoMember, canonicalizeValue); - } else { - return null; - } - } - - return coerceValueForField(context, result.build()); - } - - if (value instanceof Map) { - ImmutableMap.Builder result = ImmutableMap.builder(); - for (Map.Entry entry : ((Map) value).entrySet()) { - String name = (String) entry.getKey(); - Field field = linker.dereference(context, name); - if (field == null) { - linker.addError("unable to resolve option %s on %s", name, context.type()); - } else { - ProtoMember protoMember = ProtoMember.get(context.type(), field); - result.put(protoMember, canonicalizeValue(linker, field, entry.getValue())); - } - } - return coerceValueForField(context, result.build()); - } - - if (value instanceof List) { - ImmutableList.Builder result = ImmutableList.builder(); - for (Object element : (List) value) { - result.addAll((List) canonicalizeValue(linker, context, element)); - } - return coerceValueForField(context, result.build()); - } - - if (value instanceof String || value instanceof Boolean || value instanceof BigDecimal) { - return coerceValueForField(context, value); - } - - throw new IllegalArgumentException("Unexpected option value: " + value); - } - - private Object coerceValueForField(Field context, Object value) { - if (context.isRepeated()) { - return value instanceof List ? value : ImmutableList.of(value); - } else { - return value instanceof List ? getOnlyElement((List) value) : value; - } - } - - /** Combine values for the same key, resolving conflicts based on their type. */ - @SuppressWarnings("unchecked") - private Object union(Linker linker, Object a, Object b) { - if (a instanceof List) { - return union((List) a, (List) b); - } else if (a instanceof Map) { - return union(linker, (Map) a, (Map) b); - } else { - linker.addError("conflicting options: %s, %s", a, b); - return a; // Just return any placeholder. - } - } - - private ImmutableMap union(Linker linker, Map a, Map b) { - Map result = new LinkedHashMap<>(a); - for (Map.Entry entry : b.entrySet()) { - Object aValue = result.get(entry.getKey()); - Object bValue = entry.getValue(); - Object union = aValue != null ? union(linker, aValue, bValue) : bValue; - if (union != null) { - result.put(entry.getKey(), union); - } - } - return ImmutableMap - .copyOf(result.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); - } - - private ImmutableList union(List a, List b) { - return ImmutableList.builder().addAll(a).addAll(b).build(); - } - - Multimap fields() { - Multimap result = LinkedHashMultimap.create(); - gatherFields(result, optionType, map); - return result; - } - - private void gatherFields(Multimap sink, ProtoType type, Object o) { - if (o instanceof Map) { - for (Map.Entry entry : ((Map) o).entrySet()) { - ProtoMember protoMember = (ProtoMember) entry.getKey(); - sink.put(type, protoMember); - gatherFields(sink, protoMember.type(), entry.getValue()); - } - } else if (o instanceof List) { - for (Object e : (List) o) { - gatherFields(sink, type, e); - } - } - } - - Options retainAll(Schema schema, MarkSet markSet) { - if (map.isEmpty()) { - return this; // Nothing to prune. - } - Options result = new Options(optionType, optionElements); - Object mapOrNull = retainAll(schema, markSet, optionType, map); - result.map = mapOrNull != null ? (ImmutableMap) mapOrNull : ImmutableMap.of(); - return result; - } - - /** Returns an object of the same type as {@code o}, or null if it is not retained. */ - private Object retainAll(Schema schema, MarkSet markSet, ProtoType type, Object o) { - if (!markSet.contains(type)) { - return null; // Prune this type. - - } else if (o instanceof Map) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map.Entry entry : ((Map) o).entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toList())) { - ProtoMember protoMember = (ProtoMember) entry.getKey(); - if (!markSet.contains(protoMember)) { - continue; // Prune this field. - } - Field field = schema.getField(protoMember); - Object retainedValue = field != null ? retainAll(schema, markSet, field.type(), entry.getValue()) : null; - if (retainedValue != null) { - builder.put(protoMember, retainedValue); // This retained field is non-empty. - } - } - ImmutableMap map = builder.build(); - return !map.isEmpty() ? map : null; - - } else if (o instanceof List) { - ImmutableList.Builder builder = ImmutableList.builder(); - for (Object value : ((List) o)) { - if (value != null) { - Object retainedValue = retainAll(schema, markSet, type, value); - if (retainedValue != null) { - builder.add(retainedValue); // This retained value is non-empty. - } - - } - - } - ImmutableList list = builder.build(); - return !list.isEmpty() ? list : null; - - } else { - return o; - } - } - - public void add(OptionElement option) { - optionElements.add(option); - - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoFile.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoFile.java deleted file mode 100644 index 2395ca11..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoFile.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ -package com.squareup.wire.schema; - -import static com.squareup.wire.schema.Options.FILE_OPTIONS; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.OptionElement; -import com.squareup.wire.schema.internal.parser.ProtoFileElement; - -public final class ProtoFile { - static final ProtoMember JAVA_PACKAGE = ProtoMember.get(FILE_OPTIONS, "java_package"); - - private Location location; - private List imports = new ArrayList<>(); - private List publicImports = new ArrayList<>(); - private String packageName; - private List types = new ArrayList<>(); - private List services = new ArrayList<>(); - private List extendList = new ArrayList<>(); - private Options options; - private Syntax syntax; - private Object javaPackage; - - public ProtoFile(Location location, List imports, List publicImports, String packageName, List types, List services, - List extendList, Options options, Syntax syntax) { - this.location = location; - this.imports.addAll(imports); - this.publicImports.addAll(publicImports); - this.packageName = packageName; - this.types.addAll(types); - this.services.addAll(services); - this.extendList.addAll(extendList); - this.options = options; - this.syntax = syntax; - } - - public ProtoFile(Syntax syntax, String packageName) { - this.syntax = syntax; - this.packageName = packageName; - this.options = new Options(ProtoType.BOOL, new ArrayList()); - this.location = new Location("path", "base", 1, 1); - - } - - public void mergeFrom(ProtoFile source) { - if (this.syntax != source.syntax) { - throw new IllegalArgumentException("Source and destination protos must follow the same syntax level (proto2/proto3)"); - } - - mergeImports(source); - mergePublicImports(source); - types.addAll(source.types); - services.addAll(source.services); - } - - private void mergeImports(ProtoFile source) { - Set mergedImports = new TreeSet<>(); - mergedImports.addAll(imports); - mergedImports.addAll(source.imports); - - mergedImports.remove(location.getPath()); // Remove any imports to one self - - imports.clear(); - imports.addAll(mergedImports); - } - - private void mergePublicImports(ProtoFile source) { - Set mergedImports = new TreeSet<>(); - mergedImports.addAll(publicImports); - mergedImports.addAll(source.publicImports); - - publicImports.clear(); - publicImports.addAll(mergedImports); - } - - static ProtoFile get(ProtoFileElement protoFileElement) { - String packageName = protoFileElement.getPackageName(); - - ImmutableList types = Type.fromElements(packageName, protoFileElement.getTypes()); - - ImmutableList services = Service.fromElements(packageName, protoFileElement.getServices()); - - ImmutableList wireExtends = Extend.fromElements(packageName, protoFileElement.getExtendDeclarations()); - - Options options = new Options(Options.FILE_OPTIONS, protoFileElement.getOptions()); - - return new ProtoFile(protoFileElement.getLocation(), protoFileElement.getImports(), protoFileElement.getPublicImports(), packageName, types, services, - wireExtends, options, protoFileElement.getSyntax()); - } - - ProtoFileElement toElement() { - return new ProtoFileElement(location, packageName, syntax, imports, publicImports, Type.toElements(types), Service.toElements(services), - Extend.toElements(extendList), options.toElements()); - } - - public Location location() { - return location; - } - - public void setLocation(Location location) { - this.location = location; - } - - public List imports() { - return imports; - } - - public List publicImports() { - return publicImports; - } - - /** - * Returns the name of this proto file, like {@code simple_message} for {@code - * squareup/protos/person/simple_message.proto}. - */ - public String name() { - String result = location().getPath(); - - int slashIndex = result.lastIndexOf('/'); - if (slashIndex != -1) { - result = result.substring(slashIndex + 1); - } - - if (result.endsWith(".proto")) { - result = result.substring(0, result.length() - ".proto".length()); - } - - return result; - } - - public String packageName() { - return packageName; - } - - public String javaPackage() { - return javaPackage != null ? String.valueOf(javaPackage) : null; - } - - public List types() { - return types; - } - - public List services() { - return services; - } - - List extendList() { - return extendList; - } - - public Options options() { - return options; - } - - /** Returns a new proto file that omits types and services not in {@code identifiers}. */ - ProtoFile retainAll(Schema schema, MarkSet markSet) { - ImmutableList.Builder retainedTypes = ImmutableList.builder(); - for (Type type : types) { - Type retainedType = type.retainAll(schema, markSet); - if (retainedType != null) { - retainedTypes.add(retainedType); - } - } - - ImmutableList.Builder retainedServices = ImmutableList.builder(); - for (Service service : services) { - Service retainedService = service.retainAll(schema, markSet); - if (retainedService != null) { - retainedServices.add(retainedService); - } - } - - ProtoFile result = new ProtoFile(location, imports, publicImports, packageName, retainedTypes.build(), retainedServices.build(), extendList, - options.retainAll(schema, markSet), syntax); - result.javaPackage = javaPackage; - return result; - } - - void linkOptions(Linker linker) { - options.link(linker); - javaPackage = options().get(JAVA_PACKAGE); - } - - @Override - public String toString() { - return location().getPath(); - } - - public String toSchema() { - return toElement().toSchema(); - } - - void validate(Linker linker) { - linker.validateEnumConstantNameUniqueness(types); - } - - public Syntax getSyntax() { - return syntax; - } - - public List getExtendList() { - return extendList; - } - - /** Syntax version. */ - public enum Syntax { - PROTO_2("proto2"), - PROTO_3("proto3"); - - private final String string; - - Syntax(String string) { - this.string = string; - } - - public static Syntax get(String string) { - for (Syntax syntax : values()) { - if (syntax.string.equals(string)) { - return syntax; - } - } - throw new IllegalArgumentException("unexpected syntax: " + string); - } - - @Override - public String toString() { - return string; - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoMember.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoMember.java deleted file mode 100644 index bc87d996..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoMember.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ -package com.squareup.wire.schema; - -/** - * Identifies a field, enum or RPC on a declaring type. Members are encoded as strings containing a type name, a hash, and a member name, like - * {@code squareup.dinosaurs.Dinosaur#length_meters}. - * - *

- * A member's name is typically a simple name like "length_meters" or "packed". If the member field is an extension to its type, that name is prefixed with its - * enclosing package. This yields a member name with two packages, like {@code google.protobuf.FieldOptions#squareup.units.unit}. - */ -public final class ProtoMember { - private final ProtoType type; - private final String member; - - private ProtoMember(ProtoType type, String member) { - this.type = type; - this.member = member; - } - - public static ProtoMember get(String typeAndMember) { - int hash = typeAndMember.indexOf('#'); - if (hash == -1) { - throw new IllegalArgumentException("expected a '#' in " + typeAndMember); - } - ProtoType type = ProtoType.get(typeAndMember.substring(0, hash)); - String member = typeAndMember.substring(hash + 1); - return new ProtoMember(type, member); - } - - public static ProtoMember get(ProtoType type, String member) { - return new ProtoMember(type, member); - } - - public static ProtoMember get(ProtoType type, Field field) { - String member = field.isExtension() ? field.qualifiedName() : field.name(); - return new ProtoMember(type, member); - } - - public ProtoType type() { - return type; - } - - public String member() { - return member; - } - - @Override - public boolean equals(Object o) { - return o instanceof ProtoMember && type.equals(((ProtoMember) o).type) && member.equals(((ProtoMember) o).member); - } - - @Override - public int hashCode() { - return type.hashCode() * 37 + member.hashCode(); - } - - @Override - public String toString() { - return type + "#" + member; - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoType.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoType.java deleted file mode 100644 index 811bdf75..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/ProtoType.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -package com.squareup.wire.schema; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Names a protocol buffer message, enumerated type, service, map, or a scalar. This class models a fully-qualified name using the protocol buffer package. - */ -public final class ProtoType { - public static final ProtoType BOOL = new ProtoType(true, "bool"); - public static final ProtoType BYTES = new ProtoType(true, "bytes"); - public static final ProtoType DOUBLE = new ProtoType(true, "double"); - public static final ProtoType FLOAT = new ProtoType(true, "float"); - public static final ProtoType FIXED32 = new ProtoType(true, "fixed32"); - public static final ProtoType FIXED64 = new ProtoType(true, "fixed64"); - public static final ProtoType INT32 = new ProtoType(true, "int32"); - public static final ProtoType INT64 = new ProtoType(true, "int64"); - public static final ProtoType SFIXED32 = new ProtoType(true, "sfixed32"); - public static final ProtoType SFIXED64 = new ProtoType(true, "sfixed64"); - public static final ProtoType SINT32 = new ProtoType(true, "sint32"); - public static final ProtoType SINT64 = new ProtoType(true, "sint64"); - public static final ProtoType STRING = new ProtoType(true, "string"); - public static final ProtoType UINT32 = new ProtoType(true, "uint32"); - public static final ProtoType UINT64 = new ProtoType(true, "uint64"); - - private static final Map SCALAR_TYPES; - static { - Map scalarTypes = new LinkedHashMap<>(); - scalarTypes.put(BOOL.typeName, BOOL); - scalarTypes.put(BYTES.typeName, BYTES); - scalarTypes.put(DOUBLE.typeName, DOUBLE); - scalarTypes.put(FLOAT.typeName, FLOAT); - scalarTypes.put(FIXED32.typeName, FIXED32); - scalarTypes.put(FIXED64.typeName, FIXED64); - scalarTypes.put(INT32.typeName, INT32); - scalarTypes.put(INT64.typeName, INT64); - scalarTypes.put(SFIXED32.typeName, SFIXED32); - scalarTypes.put(SFIXED64.typeName, SFIXED64); - scalarTypes.put(SINT32.typeName, SINT32); - scalarTypes.put(SINT64.typeName, SINT64); - scalarTypes.put(STRING.typeName, STRING); - scalarTypes.put(UINT32.typeName, UINT32); - scalarTypes.put(UINT64.typeName, UINT64); - SCALAR_TYPES = Collections.unmodifiableMap(scalarTypes); - } - - private final boolean isScalar; - private final String typeName; - private final boolean isMap; - private final ProtoType keyType; - private final ProtoType valueType; - - /** Creates a scalar or message type. */ - private ProtoType(boolean isScalar, String typeName) { - checkNotNull(typeName, "typeName == null"); - this.isScalar = isScalar; - this.typeName = typeName; - this.isMap = false; - this.keyType = null; - this.valueType = null; - } - - /** Creates a map type. */ - ProtoType(ProtoType keyType, ProtoType valueType, String typeName) { - checkNotNull(keyType, "keyType == null"); - checkNotNull(valueType, "valueType == null"); - checkNotNull(typeName, "typeName == null"); - checkArgument(keyType.isScalar() && !keyType.equals(BYTES) && !keyType.equals(DOUBLE) && !keyType.equals(FLOAT), - "map key must be non-byte, non-floating point scalar: %s", keyType); - this.isScalar = false; - this.typeName = typeName; - this.isMap = true; - this.keyType = keyType; // TODO restrict what's allowed here - this.valueType = valueType; - } - - public String simpleName() { - int dot = typeName.lastIndexOf('.'); - return typeName.substring(dot + 1); - } - - /** Returns the enclosing type, or null if this type is not nested in another type. */ - public String enclosingTypeOrPackage() { - int dot = typeName.lastIndexOf('.'); - return dot == -1 ? null : typeName.substring(0, dot); - } - - public boolean isScalar() { - return isScalar; - } - - public boolean isMap() { - return isMap; - } - - /** The type of the map's keys. Only present when {@link #isMap} is true. */ - public ProtoType keyType() { - return keyType; - } - - /** The type of the map's values. Only present when {@link #isMap} is true. */ - public ProtoType valueType() { - return valueType; - } - - public static ProtoType get(String enclosingTypeOrPackage, String typeName) { - return enclosingTypeOrPackage != null ? get(enclosingTypeOrPackage + '.' + typeName) : get(typeName); - } - - public static ProtoType get(String name) { - ProtoType scalar = SCALAR_TYPES.get(name); - if (scalar != null) { - return scalar; - } - - if (name == null || name.isEmpty() || name.contains("#")) { - throw new IllegalArgumentException("unexpected name: " + name); - } - - if (name.startsWith("map<") && name.endsWith(">")) { - int comma = name.indexOf(','); - if (comma == -1) { - throw new IllegalArgumentException("expected ',' in map type: " + name); - } - ProtoType key = get(name.substring(4, comma).trim()); - ProtoType value = get(name.substring(comma + 1, name.length() - 1).trim()); - return new ProtoType(key, value, name); - } - - return new ProtoType(false, name); - } - - public ProtoType nestedType(String name) { - if (isScalar) { - throw new UnsupportedOperationException("scalar cannot have a nested type"); - } - if (isMap) { - throw new UnsupportedOperationException("map cannot have a nested type"); - } - if (name == null || name.contains(".") || name.isEmpty()) { - throw new IllegalArgumentException("unexpected name: " + name); - } - return new ProtoType(false, typeName + '.' + name); - } - - @Override - public boolean equals(Object o) { - return o instanceof ProtoType && typeName.equals(((ProtoType) o).typeName); - } - - @Override - public int hashCode() { - return typeName.hashCode(); - } - - @Override - public String toString() { - return typeName; - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Pruner.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Pruner.java deleted file mode 100644 index 44679966..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Pruner.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.ImmutableList; - -/** - * Creates a new schema that contains only the types selected by an identifier set, including their transitive dependencies. - */ -final class Pruner { - final Schema schema; - final IdentifierSet identifierSet; - final MarkSet marks; - - /** - * {@link ProtoType types} and {@link ProtoMember members} whose immediate dependencies have not yet been visited. - */ - final Deque queue; - - Pruner(Schema schema, IdentifierSet identifierSet) { - this.schema = schema; - this.identifierSet = identifierSet; - this.marks = new MarkSet(identifierSet); - this.queue = new ArrayDeque<>(); - } - - public Schema prune() { - markRoots(); - markReachable(); - - ImmutableList.Builder retained = ImmutableList.builder(); - for (ProtoFile protoFile : schema.protoFiles()) { - retained.add(protoFile.retainAll(schema, marks)); - } - - return new Schema(retained.build()); - } - - private void markRoots() { - for (ProtoFile protoFile : schema.protoFiles()) { - markRoots(protoFile); - } - } - - private void markRoots(ProtoFile protoFile) { - for (Type type : protoFile.types()) { - markRoots(type); - } - for (Service service : protoFile.services()) { - markRoots(service); - } - } - - private void markRoots(Type type) { - ProtoType protoType = type.type(); - if (identifierSet.includes(protoType)) { - marks.root(protoType); - queue.add(protoType); - } else { - if (type instanceof MessageType) { - for (Field field : ((MessageType) type).fieldsAndOneOfFields()) { - markRoots(ProtoMember.get(protoType, field.name())); - } - } else if (type instanceof EnumType) { - for (EnumConstant enumConstant : ((EnumType) type).constants()) { - markRoots(ProtoMember.get(protoType, enumConstant.getName())); - } - } else { - throw new AssertionError(); - } - } - - for (Type nested : type.nestedTypes()) { - markRoots(nested); - } - } - - private void markRoots(Service service) { - ProtoType protoType = service.type(); - if (identifierSet.includes(protoType)) { - marks.root(protoType); - queue.add(protoType); - } else { - for (Rpc rpc : service.rpcs()) { - markRoots(ProtoMember.get(protoType, rpc.name())); - } - } - } - - private void markRoots(ProtoMember protoMember) { - if (identifierSet.includes(protoMember)) { - marks.root(protoMember); - queue.add(protoMember); - } - } - - private void markReachable() { - // Mark everything reachable by what's enqueued, queueing new things as we go. - for (Object root; (root = queue.poll()) != null;) { - if (root instanceof ProtoMember) { - ProtoMember protoMember = (ProtoMember) root; - mark(protoMember.type()); - String member = ((ProtoMember) root).member(); - Type type = schema.getType(protoMember.type()); - if (type instanceof MessageType) { - Field field = ((MessageType) type).field(member); - if (field == null) { - field = ((MessageType) type).extensionField(member); - } - if (field != null) { - markField(type.type(), field); - continue; - } - } else if (type instanceof EnumType) { - EnumConstant constant = ((EnumType) type).constant(member); - if (constant != null) { - markOptions(constant.getOptions()); - continue; - } - } - - Service service = schema.getService(protoMember.type()); - if (service != null) { - Rpc rpc = service.rpc(member); - if (rpc != null) { - markRpc(service.type(), rpc); - continue; - } - } - - throw new IllegalArgumentException("Unexpected member: " + root); - - } else if (root instanceof ProtoType) { - ProtoType protoType = (ProtoType) root; - if (protoType.isScalar()) { - continue; // Skip scalar types. - } - - Type type = schema.getType(protoType); - if (type != null) { - markType(type); - continue; - } - - Service service = schema.getService(protoType); - if (service != null) { - markService(service); - continue; - } - - throw new IllegalArgumentException("Unexpected type: " + root); - - } else { - throw new AssertionError(); - } - } - } - - private void mark(ProtoType type) { - // Mark the map type as it's non-scalar and transitively reachable. - if (type.isMap()) { - marks.mark(type); - // Map key type is always scalar. No need to mark it. - type = type.valueType(); - } - - if (marks.mark(type)) { - queue.add(type); // The transitive dependencies of this type must be visited. - } - } - - private void mark(ProtoMember protoMember) { - if (marks.mark(protoMember)) { - queue.add(protoMember); // The transitive dependencies of this member must be visited. - } - } - - private void markType(Type type) { - markOptions(type.options()); - - if (marks.containsAllMembers(type.type())) { - if (type instanceof MessageType) { - markMessage((MessageType) type); - } else if (type instanceof EnumType) { - markEnum((EnumType) type); - } - } - } - - private void markMessage(MessageType message) { - markFields(message.type(), message.fields()); - for (OneOf oneOf : message.oneOfs()) { - markFields(message.type(), oneOf.fields()); - } - } - - private void markEnum(EnumType wireEnum) { - markOptions(wireEnum.options()); - if (marks.containsAllMembers(wireEnum.type())) { - for (EnumConstant constant : wireEnum.constants()) { - if (marks.contains(ProtoMember.get(wireEnum.type(), constant.getName()))) { - markOptions(constant.getOptions()); - } - } - } - } - - private void markFields(ProtoType declaringType, List fields) { - for (Field field : fields) { - markField(declaringType, field); - } - } - - private void markField(ProtoType declaringType, Field field) { - if (marks.contains(ProtoMember.get(declaringType, field.name()))) { - markOptions(field.options()); - mark(field.type()); - } - } - - private void markOptions(Options options) { - for (Map.Entry entry : options.fields().entries()) { - mark(entry.getValue()); - } - } - - private void markService(Service service) { - markOptions(service.options()); - if (marks.containsAllMembers(service.type())) { - for (Rpc rpc : service.rpcs()) { - markRpc(service.type(), rpc); - } - } - } - - private void markRpc(ProtoType declaringType, Rpc rpc) { - if (marks.contains(ProtoMember.get(declaringType, rpc.name()))) { - markOptions(rpc.options()); - mark(rpc.requestType()); - mark(rpc.responseType()); - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Reserved.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Reserved.kt deleted file mode 100644 index 45f3dd52..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Reserved.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema - -import com.google.common.collect.Range -import com.squareup.wire.schema.internal.parser.ReservedElement - -class Reserved( - val location: Location, - val documentation: String, - val values: List -) { - fun matchesTag(tag: Int) = values.any { - it is Int && tag == it || (it as? Range)?.contains(tag) == true - } - - fun matchesName(name: String) = values.any { it is String && name == it } - - companion object { - @JvmStatic - fun fromElements(elements: List) = - elements.map { Reserved(it.location, it.documentation, it.values) } - - @JvmStatic - fun toElements(reserveds: List) = - reserveds.map { ReservedElement(it.location, it.documentation, it.values) } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Rpc.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Rpc.java deleted file mode 100644 index 044c4d88..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Rpc.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.RpcElement; - -public final class Rpc { - private final Location location; - private final String name; - private final String documentation; - private final String requestTypeElement; - private final String responseTypeElement; - private final Options options; - private ProtoType requestType; - private ProtoType responseType; - private final boolean requestStreaming; - private final boolean responseStreaming; - - private Rpc(Location location, String name, String documentation, String requestType, String responseType, boolean requestStreaming, - boolean responseStreaming, Options options) { - this.location = location; - this.name = name; - this.documentation = documentation; - this.requestTypeElement = requestType; - this.responseTypeElement = responseType; - this.requestStreaming = requestStreaming; - this.responseStreaming = responseStreaming; - this.options = options; - } - - public Location location() { - return location; - } - - public String name() { - return name; - } - - public String documentation() { - return documentation; - } - - public ProtoType requestType() { - return requestType; - } - - public ProtoType responseType() { - return responseType; - } - - public boolean requestStreaming() { - return requestStreaming; - } - - public boolean responseStreaming() { - return responseStreaming; - } - - public Options options() { - return options; - } - - void link(Linker linker) { - linker = linker.withContext(this); - requestType = linker.resolveMessageType(requestTypeElement); - responseType = linker.resolveMessageType(responseTypeElement); - } - - void linkOptions(Linker linker) { - linker = linker.withContext(this); - options.link(linker); - } - - void validate(Linker linker) { - linker = linker.withContext(this); - linker.validateImport(location(), requestType); - linker.validateImport(location(), responseType); - } - - Rpc retainAll(Schema schema, MarkSet markSet) { - if (!markSet.contains(requestType) || !markSet.contains(responseType)) - return null; - Rpc result = new Rpc(location, name, documentation, requestTypeElement, responseTypeElement, requestStreaming, responseStreaming, - options.retainAll(schema, markSet)); - result.requestType = requestType; - result.responseType = responseType; - return result; - } - - static ImmutableList fromElements(List elements) { - ImmutableList.Builder rpcs = new ImmutableList.Builder<>(); - for (RpcElement rpcElement : elements) { - rpcs.add(new Rpc(rpcElement.getLocation(), rpcElement.getName(), rpcElement.getDocumentation(), rpcElement.getRequestType(), - rpcElement.getResponseType(), rpcElement.getRequestStreaming(), rpcElement.getResponseStreaming(), - new Options(Options.METHOD_OPTIONS, rpcElement.getOptions()))); - } - return rpcs.build(); - } - - static ImmutableList toElements(ImmutableList rpcs) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (Rpc rpc : rpcs) { - elements.add(new RpcElement(rpc.location, rpc.name, rpc.documentation, rpc.requestTypeElement, rpc.responseTypeElement, rpc.requestStreaming, - rpc.responseStreaming, rpc.options.toElements())); - } - return elements.build(); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Schema.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Schema.java deleted file mode 100644 index 76af7f63..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Schema.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.LinkedHashMap; -import java.util.Map; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Ordering; -import com.squareup.wire.ProtoAdapter; - -/** - * A collection of .proto files that describe a set of messages. A schema is linked: each field's type name is resolved to the corresponding type - * definition. - * - *

- * Use {@link SchemaLoader} to load a schema from source files. - */ -public final class Schema { - private static final Ordering PATH_ORDER = new Ordering() { - @Override - public int compare(ProtoFile left, ProtoFile right) { - return left.location().getPath().compareTo(right.location().getPath()); - } - }; - - private final ImmutableList protoFiles; - private final ImmutableMap typesIndex; - private final ImmutableMap servicesIndex; - - Schema(Iterable protoFiles) { - this.protoFiles = PATH_ORDER.immutableSortedCopy(protoFiles); - this.typesIndex = buildTypesIndex(protoFiles); - this.servicesIndex = buildServicesIndex(protoFiles); - } - - public ImmutableList protoFiles() { - return protoFiles; - } - - /** Returns the proto file at {@code path}, or null if this schema has no such file. */ - public ProtoFile protoFile(String path) { - for (ProtoFile protoFile : protoFiles) { - if (protoFile.location().getPath().equals(path)) { - return protoFile; - } - } - return null; - } - - public ProtoFile protoFileForPackage(String packageName) { - for (ProtoFile protoFile : protoFiles) { - if (packageName == null && protoFile.packageName() == null) { - return protoFile; - } else if (packageName == null || protoFile.packageName() == null) { - continue; - } else if (packageName.equals(protoFile.packageName())) { - return protoFile; - } - } - return null; - } - - /** - * Returns a copy of this schema that retains only the types and services selected by {@code - * identifierSet}, plus their transitive dependencies. - */ - public Schema prune(IdentifierSet identifierSet) { - return new Pruner(this, identifierSet).prune(); - } - - /** - * Returns the service with the fully qualified name {@code name}, or null if this schema defines no such service. - */ - public Service getService(String name) { - return servicesIndex.get(name); - } - - /** - * Returns the service with the fully qualified name {@code name}, or null if this schema defines no such service. - */ - public Service getService(ProtoType protoType) { - return getService(protoType.toString()); - } - - /** - * Returns the type with the fully qualified name {@code name}, or null if this schema defines no such type. - */ - public Type getType(String name) { - return typesIndex.get(name); - } - - /** - * Returns the type with the fully qualified name {@code name}, or null if this schema defines no such type. - */ - public Type getType(ProtoType protoType) { - return getType(protoType.toString()); - } - - public Field getField(ProtoMember protoMember) { - Type type = getType(protoMember.type()); - if (!(type instanceof MessageType)) - return null; - Field field = ((MessageType) type).field(protoMember.member()); - if (field == null) { - field = ((MessageType) type).extensionField(protoMember.member()); - } - return field; - } - - private static ImmutableMap buildTypesIndex(Iterable protoFiles) { - Map result = new LinkedHashMap<>(); - for (ProtoFile protoFile : protoFiles) { - for (Type type : protoFile.types()) { - index(result, type); - } - } - return ImmutableMap.copyOf(result); - } - - private static void index(Map typesByName, Type type) { - typesByName.put(type.type().toString(), type); - for (Type nested : type.nestedTypes()) { - index(typesByName, nested); - } - } - - private static ImmutableMap buildServicesIndex(Iterable protoFiles) { - ImmutableMap.Builder result = ImmutableMap.builder(); - for (ProtoFile protoFile : protoFiles) { - for (Service service : protoFile.services()) { - result.put(service.type().toString(), service); - } - } - return result.build(); - } - - /** - * Returns a wire adapter for the message or enum type named {@code typeName}. The returned type adapter doesn't have model classes to encode and decode - * from, so instead it uses scalar types ({@linkplain String}, {@linkplain okio.ByteString ByteString}, {@linkplain Integer}, etc.), {@linkplain Map maps}, - * and {@linkplain java.util.List lists}. It can both encode and decode these objects. Map keys are field names. - * - * @param includeUnknown true to include values for unknown tags in the returned model. Map keys for such values is the unknown value's tag name as a - * string. Unknown values are decoded to {@linkplain Long}, {@linkplain Long}, {@linkplain Integer}, or {@linkplain okio.ByteString - * ByteString} for {@linkplain com.squareup.wire.FieldEncoding#VARINT VARINT}, {@linkplain com.squareup.wire.FieldEncoding#FIXED64 - * FIXED64}, {@linkplain com.squareup.wire.FieldEncoding#FIXED32 FIXED32}, or - * {@linkplain com.squareup.wire.FieldEncoding#LENGTH_DELIMITED LENGTH_DELIMITED} respectively. - */ - public ProtoAdapter protoAdapter(String typeName, boolean includeUnknown) { - Type type = getType(typeName); - if (type == null) - throw new IllegalArgumentException("unexpected type " + typeName); - return new SchemaProtoAdapterFactory(this, includeUnknown).get(type.type()); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaException.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaException.java deleted file mode 100644 index 47e5fdfd..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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. - */ -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ -package com.squareup.wire.schema; - -import java.util.List; - -public final class SchemaException extends RuntimeException { - public SchemaException(List errors) { - super(String.join("\n", errors)); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaLoader.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaLoader.java deleted file mode 100644 index 6f4f29e2..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaLoader.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static com.google.common.collect.Iterables.getOnlyElement; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.google.common.io.Closer; -import com.squareup.wire.schema.internal.parser.ProtoFileElement; -import com.squareup.wire.schema.internal.parser.ProtoParser; - -import okio.BufferedSource; -import okio.Okio; -import okio.Source; - -/** - * Load proto files and their transitive dependencies, parse them, and link them together. - * - *

- * To find proto files to load, a non-empty set of sources are searched. Each source is either a regular directory or a ZIP file. Within ZIP files, proto files - * are expected to be found relative to the root of the archive. - */ -public final class SchemaLoader { - static final String DESCRIPTOR_PROTO = "google/protobuf/descriptor.proto"; - - private final List sources = new ArrayList<>(); - private final List protos = new ArrayList<>(); - - /** Add directory or zip file source from which proto files will be loaded. */ - public SchemaLoader addSource(File file) { - return addSource(file.toPath()); - } - - /** Add directory or zip file source from which proto files will be loaded. */ - public SchemaLoader addSource(Path path) { - sources.add(path); - return this; - } - - /** Returns a mutable list of the sources that this loader will load from. */ - public List sources() { - return sources; - } - - /** - * Add a proto file to load. Dependencies will be loaded automatically from the configured sources. - */ - public SchemaLoader addProto(String proto) { - protos.add(proto); - return this; - } - - /** Returns a mutable list of the protos that this loader will load. */ - public List protos() { - return protos; - } - - public Schema load() throws IOException { - if (sources.isEmpty()) { - throw new IllegalStateException("No sources added."); - } - - try (Closer closer = Closer.create()) { - // Map the physical path to the file system root. For regular directories the key and the - // value are equal. For ZIP files the key is the path to the .zip, and the value is the root - // of the file system within it. - Map directories = new LinkedHashMap<>(); - for (Path source : sources) { - if (Files.isRegularFile(source)) { - FileSystem sourceFs = FileSystems.newFileSystem(source, getClass().getClassLoader()); - closer.register(sourceFs); - directories.put(source, getOnlyElement(sourceFs.getRootDirectories())); - } else { - directories.put(source, source); - } - } - return loadFromDirectories(directories); - } - } - - private Schema loadFromDirectories(Map directories) throws IOException { - final Deque protos = new ArrayDeque<>(this.protos); - if (protos.isEmpty()) { - for (final Map.Entry entry : directories.entrySet()) { - Files.walkFileTree(entry.getValue(), new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.getFileName().toString().endsWith(".proto")) { - protos.add(entry.getValue().relativize(file).toString()); - } - return FileVisitResult.CONTINUE; - } - }); - } - } - - Map loaded = new LinkedHashMap<>(); - loaded.put(DESCRIPTOR_PROTO, loadDescriptorProto()); - - while (!protos.isEmpty()) { - String proto = protos.removeFirst(); - if (loaded.containsKey(proto)) { - continue; - } - - ProtoFileElement element = null; - for (Map.Entry entry : directories.entrySet()) { - Source source = source(entry.getValue(), proto); - if (source == null) { - continue; - } - - Path base = entry.getKey(); - try { - Location location = Location.get(base.toString(), proto); - String data = Okio.buffer(source).readUtf8(); - element = ProtoParser.parse(location, data); - break; - } catch (IOException e) { - throw new IOException("Failed to load " + proto + " from " + base, e); - } finally { - source.close(); - } - } - if (element == null) { - throw new FileNotFoundException("Failed to locate " + proto + " in " + sources); - } - - ProtoFile protoFile = ProtoFile.get(element); - loaded.put(proto, protoFile); - - // Queue dependencies to be loaded. - for (String importPath : element.getImports()) { - protos.addLast(importPath); - } - } - - return new Linker(loaded.values()).link(); - } - - /** - * Returns Google's protobuf descriptor, which defines standard options like default, deprecated, and java_package. If the user has provided their own - * version of the descriptor proto, that is preferred. - */ - private ProtoFile loadDescriptorProto() throws IOException { - InputStream resourceAsStream = SchemaLoader.class.getResourceAsStream("/" + DESCRIPTOR_PROTO); - try (BufferedSource buffer = Okio.buffer(Okio.source(resourceAsStream))) { - String data = buffer.readUtf8(); - Location location = Location.get("", DESCRIPTOR_PROTO); - ProtoFileElement element = ProtoParser.parse(location, data); - return ProtoFile.get(element); - } - } - - private static Source source(Path base, String path) throws IOException { - Path resolvedPath = base.resolve(path); - if (Files.exists(resolvedPath)) { - return Okio.source(resolvedPath); - } - return null; - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaProtoAdapterFactory.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaProtoAdapterFactory.java deleted file mode 100644 index bfaa2212..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/SchemaProtoAdapterFactory.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.squareup.wire.FieldEncoding; -import com.squareup.wire.ProtoAdapter; -import com.squareup.wire.ProtoReader; -import com.squareup.wire.ProtoWriter; - -/** - * Creates type adapters to read and write protocol buffer data from a schema model. This doesn't require an intermediate code gen step. - */ -final class SchemaProtoAdapterFactory { - final Schema schema; - final boolean includeUnknown; - final Map> adapterMap = new LinkedHashMap<>(); - - SchemaProtoAdapterFactory(Schema schema, boolean includeUnknown) { - this.schema = schema; - this.includeUnknown = includeUnknown; - - adapterMap.put(ProtoType.BOOL, ProtoAdapter.BOOL); - adapterMap.put(ProtoType.BYTES, ProtoAdapter.BYTES); - adapterMap.put(ProtoType.DOUBLE, ProtoAdapter.DOUBLE); - adapterMap.put(ProtoType.FLOAT, ProtoAdapter.FLOAT); - adapterMap.put(ProtoType.FIXED32, ProtoAdapter.FIXED32); - adapterMap.put(ProtoType.FIXED64, ProtoAdapter.FIXED64); - adapterMap.put(ProtoType.INT32, ProtoAdapter.INT32); - adapterMap.put(ProtoType.INT64, ProtoAdapter.INT64); - adapterMap.put(ProtoType.SFIXED32, ProtoAdapter.SFIXED32); - adapterMap.put(ProtoType.SFIXED64, ProtoAdapter.SFIXED64); - adapterMap.put(ProtoType.SINT32, ProtoAdapter.SINT32); - adapterMap.put(ProtoType.SINT64, ProtoAdapter.SINT64); - adapterMap.put(ProtoType.STRING, ProtoAdapter.STRING); - adapterMap.put(ProtoType.UINT32, ProtoAdapter.UINT32); - adapterMap.put(ProtoType.UINT64, ProtoAdapter.UINT64); - } - - public ProtoAdapter get(ProtoType protoType) { - if (protoType.isMap()) - throw new UnsupportedOperationException("map types not supported"); - - ProtoAdapter result = adapterMap.get(protoType); - if (result != null) { - return (ProtoAdapter) result; - } - - Type type = schema.getType(protoType); - if (type == null) { - throw new IllegalArgumentException("unknown type: " + protoType); - } - - if (type instanceof EnumType) { - EnumAdapter enumAdapter = new EnumAdapter((EnumType) type); - adapterMap.put(protoType, enumAdapter); - return enumAdapter; - } - - if (type instanceof MessageType) { - MessageAdapter messageAdapter = new MessageAdapter(includeUnknown); - // Put the adapter in the map early to mitigate the recursive calls to get() made below. - adapterMap.put(protoType, messageAdapter); - - for (com.squareup.wire.schema.Field field : ((MessageType) type).fields()) { - Field fieldAdapter = new Field(field.name(), field.tag(), field.isRepeated(), get(field.type())); - messageAdapter.fieldsByName.put(field.name(), fieldAdapter); - messageAdapter.fieldsByTag.put(field.tag(), fieldAdapter); - } - return (ProtoAdapter) messageAdapter; - } - - throw new IllegalArgumentException("unexpected type: " + protoType); - } - - static final class EnumAdapter extends ProtoAdapter { - final EnumType enumType; - - EnumAdapter(EnumType enumType) { - super(FieldEncoding.VARINT, (Class) null); // null == JvmClassMappingKt.getKotlinClass(Object.class) - this.enumType = enumType; - } - - @Override - public int encodedSize(Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public void encode(ProtoWriter writer, Object value) throws IOException { - if (value instanceof String) { - EnumConstant constant = enumType.constant((String) value); - writer.writeVarint32(constant.getTag()); - } else if (value instanceof Integer) { - writer.writeVarint32((Integer) value); - } else { - throw new IllegalArgumentException("unexpected " + enumType.type() + ": " + value); - } - } - - @Override - public Object decode(ProtoReader reader) throws IOException { - Integer value = ProtoAdapter.UINT32.decode(reader); - EnumConstant constant = enumType.constant(value); - return constant != null ? constant.getName() : value; - } - - @Override - public Object redact(Object value) { - throw new UnsupportedOperationException(); - } - } - - static final class MessageAdapter extends ProtoAdapter> { - final Map fieldsByTag = new LinkedHashMap<>(); - final Map fieldsByName = new LinkedHashMap<>(); - final boolean includeUnknown; - - MessageAdapter(boolean includeUnknown) { - super(FieldEncoding.LENGTH_DELIMITED, (Class) null); // null = JvmClassMappingKt.getKotlinClass(Map.class) - this.includeUnknown = includeUnknown; - } - - @Override - public Map redact(Map message) { - throw new UnsupportedOperationException(); - } - - @Override - public int encodedSize(Map value) { - int size = 0; - for (Map.Entry entry : value.entrySet()) { - Field field = fieldsByName.get(entry.getKey()); - if (field == null) - continue; // Ignore unknown values! - - ProtoAdapter protoAdapter = (ProtoAdapter) field.protoAdapter; - if (field.repeated) { - for (Object o : (List) entry.getValue()) { - size += protoAdapter.encodedSizeWithTag(field.tag, o); - } - } else { - size += protoAdapter.encodedSizeWithTag(field.tag, entry.getValue()); - } - } - return size; - } - - @Override - public void encode(ProtoWriter writer, Map value) throws IOException { - for (Map.Entry entry : value.entrySet()) { - Field field = fieldsByName.get(entry.getKey()); - if (field == null) - continue; // Ignore unknown values! - - ProtoAdapter protoAdapter = (ProtoAdapter) field.protoAdapter; - if (field.repeated) { - for (Object o : (List) entry.getValue()) { - protoAdapter.encodeWithTag(writer, field.tag, o); - } - } else { - protoAdapter.encodeWithTag(writer, field.tag, entry.getValue()); - } - } - } - - @Override - public Map decode(ProtoReader reader) throws IOException { - Map result = new LinkedHashMap<>(); - - long token = reader.beginMessage(); - for (int tag; (tag = reader.nextTag()) != -1;) { - Field field = fieldsByTag.get(tag); - if (field == null) { - if (includeUnknown) { - String name = Integer.toString(tag); - field = new Field(name, tag, true, reader.peekFieldEncoding().rawProtoAdapter()); - } else { - reader.skip(); - continue; - } - } - - Object value = field.protoAdapter.decode(reader); - if (field.repeated) { - List values = (List) result.get(field.name); - if (values == null) { - values = new ArrayList<>(); - result.put(field.name, values); - } - values.add(value); - } else { - result.put(field.name, value); - } - } - reader.endMessage(token); - return result; - } - - @Override - public String toString(Map value) { - throw new UnsupportedOperationException(); - } - } - - static class Field { - final String name; - final int tag; - final boolean repeated; - final ProtoAdapter protoAdapter; - - Field(String name, int tag, boolean repeated, ProtoAdapter protoAdapter) { - this.name = name; - this.tag = tag; - this.repeated = repeated; - this.protoAdapter = protoAdapter; - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Service.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Service.java deleted file mode 100644 index 4bb0013c..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Service.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.ServiceElement; - -public final class Service { - private final ProtoType protoType; - private final Location location; - private final String name; - private final String documentation; - private final ImmutableList rpcs; - private final Options options; - - private Service(ProtoType protoType, Location location, String documentation, String name, ImmutableList rpcs, Options options) { - this.protoType = protoType; - this.location = location; - this.documentation = documentation; - this.name = name; - this.rpcs = rpcs; - this.options = options; - } - - static Service fromElement(ProtoType protoType, ServiceElement element) { - ImmutableList rpcs = Rpc.fromElements(element.getRpcs()); - Options options = new Options(Options.SERVICE_OPTIONS, element.getOptions()); - - return new Service(protoType, element.getLocation(), element.getDocumentation(), element.getName(), rpcs, options); - } - - public Location location() { - return location; - } - - public ProtoType type() { - return protoType; - } - - public String documentation() { - return documentation; - } - - public String name() { - return name; - } - - public ImmutableList rpcs() { - return rpcs; - } - - /** Returns the RPC named {@code name}, or null if this service has no such method. */ - public Rpc rpc(String name) { - for (Rpc rpc : rpcs) { - if (rpc.name().equals(name)) { - return rpc; - } - } - return null; - } - - public Options options() { - return options; - } - - void link(Linker linker) { - linker = linker.withContext(this); - for (Rpc rpc : rpcs) { - rpc.link(linker); - } - } - - void linkOptions(Linker linker) { - linker = linker.withContext(this); - for (Rpc rpc : rpcs) { - rpc.linkOptions(linker); - } - options.link(linker); - } - - void validate(Linker linker) { - linker = linker.withContext(this); - for (Rpc rpc : rpcs) { - rpc.validate(linker); - } - } - - Service retainAll(Schema schema, MarkSet markSet) { - // If this service is not retained, prune it. - if (!markSet.contains(protoType)) { - return null; - } - - ImmutableList.Builder retainedRpcs = ImmutableList.builder(); - for (Rpc rpc : rpcs) { - Rpc retainedRpc = rpc.retainAll(schema, markSet); - if (retainedRpc != null && markSet.contains(ProtoMember.get(protoType, rpc.name()))) { - retainedRpcs.add(retainedRpc); - } - } - - return new Service(protoType, location, documentation, name, retainedRpcs.build(), options.retainAll(schema, markSet)); - } - - static ImmutableList fromElements(String packageName, List elements) { - ImmutableList.Builder services = ImmutableList.builder(); - for (ServiceElement service : elements) { - ProtoType protoType = ProtoType.get(packageName, service.getName()); - services.add(Service.fromElement(protoType, service)); - } - return services.build(); - } - - static ImmutableList toElements(List services) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (Service service : services) { - elements.add(new ServiceElement(service.location, service.name, service.documentation, Rpc.toElements(service.rpcs), service.options.toElements())); - } - return elements.build(); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Type.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/Type.java deleted file mode 100644 index 88a22cf6..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/Type.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.EnumElement; -import com.squareup.wire.schema.internal.parser.MessageElement; -import com.squareup.wire.schema.internal.parser.TypeElement; - -public abstract class Type { - public abstract Location location(); - - public abstract ProtoType type(); - - public abstract String documentation(); - - public abstract Options options(); - - public abstract List nestedTypes(); - - abstract void link(Linker linker); - - abstract void linkOptions(Linker linker); - - abstract void validate(Linker linker); - - abstract Type retainAll(Schema schema, MarkSet markSet); - - public static Type get(String packageName, ProtoType protoType, TypeElement type) { - if (type instanceof EnumElement) { - return EnumType.fromElement(protoType, (EnumElement) type); - } else if (type instanceof MessageElement) { - return MessageType.fromElement(packageName, protoType, (MessageElement) type); - } else { - throw new IllegalArgumentException("unexpected type: " + type.getClass()); - } - } - - static ImmutableList fromElements(String packageName, List elements) { - ImmutableList.Builder types = new ImmutableList.Builder<>(); - for (TypeElement element : elements) { - ProtoType protoType = ProtoType.get(packageName, element.getName()); - types.add(Type.get(packageName, protoType, element)); - } - return types.build(); - } - - static TypeElement toElement(Type type) { - if (type instanceof EnumType) { - return ((EnumType) type).toElement(); - } else if (type instanceof MessageType) { - return ((MessageType) type).toElement(); - } else if (type instanceof EnclosingType) { - return ((EnclosingType) type).toElement(); - } else { - throw new IllegalArgumentException("unexpected type: " + type.getClass()); - } - } - - static ImmutableList toElements(List types) { - ImmutableList.Builder elements = new ImmutableList.Builder<>(); - for (Type type : types) { - elements.add(Type.toElement(type)); - } - return elements.build(); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/Util.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/Util.java deleted file mode 100644 index 1390c69d..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/Util.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 - 2020 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.squareup.wire.schema.internal.parser.OptionElement; - -public final class Util { - public static final int MIN_TAG_VALUE = 1; - public static final int MAX_TAG_VALUE = (1 << 29) - 1; // 536,870,911 - private static final int RESERVED_TAG_VALUE_START = 19000; - private static final int RESERVED_TAG_VALUE_END = 19999; - - public static void appendDocumentation(StringBuilder builder, String documentation) { - if (documentation.isEmpty()) { - return; - } - for (String line : documentation.split("\n")) { - builder.append("// ").append(line).append('\n'); - } - } - - public static void appendOptions(StringBuilder builder, List options) { - builder.append("[\n"); - for (int i = 0, count = options.size(); i < count; i++) { - String endl = (i < count - 1) ? "," : ""; - appendIndented(builder, options.get(i).toSchema() + endl); - } - builder.append(']'); - } - - public static void appendIndented(StringBuilder builder, String value) { - for (String line : value.split("\n")) { - builder.append(" ").append(line).append('\n'); - } - } - - /** True if the supplied value is in the valid tag range and not reserved. */ - public static boolean isValidTag(int value) { - return (value >= MIN_TAG_VALUE && value < RESERVED_TAG_VALUE_START) || (value > RESERVED_TAG_VALUE_END && value <= MAX_TAG_VALUE); - } - - public static ImmutableList concatenate(List a, T b) { - return ImmutableList.builder().addAll(a).add(b).build(); - } - - private Util() { - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumConstantElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumConstantElement.kt deleted file mode 100644 index a37381b6..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumConstantElement.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.Util.appendDocumentation - -data class EnumConstantElement( - val location: Location, - val name: String, - val tag: Int, - val documentation: String = "", - val options: List = emptyList() -) { - - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("$name = $tag") - - if (options.isNotEmpty()) { - append(" ") - Util.appendOptions(this, options) - } - append(";\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumElement.kt deleted file mode 100644 index 4c38bb69..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/EnumElement.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class EnumElement( - override val location: Location, - override val name: String, - override val documentation: String = "", - override val options: List = emptyList(), - val reserveds: List = emptyList(), - val constants: List = emptyList() -) : TypeElement { - // Enums do not allow nested type declarations. - override val nestedTypes: List = emptyList() - - override fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("enum $name {") - - if (reserveds.isNotEmpty()) { - append('\n') - for (reserved in reserveds) { - appendIndented(this, reserved.toSchema()) - } - } - - if (options.isNotEmpty()) { - append('\n') - for (option in options) { - appendIndented(this, option.toSchemaDeclaration()) - } - } - if (constants.isNotEmpty()) { - append('\n') - for (constant in constants) { - appendIndented(this, constant.toSchema()) - } - } - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtendElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtendElement.kt deleted file mode 100644 index f3d819a8..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtendElement.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class ExtendElement( - val location: Location, - val name: String, - val documentation: String = "", - val fields: List = emptyList() -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("extend $name {") - - if (fields.isNotEmpty()) { - append('\n') - for (field in fields) { - appendIndented(this, field.toSchema()) - } - } - - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtensionsElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtensionsElement.kt deleted file mode 100644 index ce6cda89..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ExtensionsElement.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.Util.appendDocumentation - -data class ExtensionsElement( - val location: Location, - val documentation: String = "", - val start: Int, - val end: Int -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("extensions $start") - - if (start != end) { - append(" to ") - if (end < Util.MAX_TAG_VALUE) { - append(end) - } else { - append("max") - } - } - append(";\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/FieldElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/FieldElement.kt deleted file mode 100644 index 6df685fd..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/FieldElement.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Field -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.Util.appendDocumentation -import java.util.* - -data class FieldElement( - val location: Location, - val label: Field.Label? = null, - val type: String, - val name: String, - val defaultValue: String? = null, - val tag: Int = 0, - val documentation: String = "", - val options: List = emptyList() -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - - if (label != null) { - append("${label.name.lowercase(Locale.US)} ") - } - append("$type $name = $tag") - - if (options.isNotEmpty()) { - append(' ') - Util.appendOptions(this, options) - } - - append(";\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/GroupElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/GroupElement.kt deleted file mode 100644 index 28f67e69..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/GroupElement.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Field -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented -import java.util.* - -data class GroupElement( - val label: Field.Label? = null, - val location: Location, - val name: String, - val tag: Int, - val documentation: String = "", - val fields: List = emptyList() -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - if (label != null) { - append("${label.name.lowercase(Locale.US)} ") - } - append("group $name = $tag {") - if (fields.isNotEmpty()) { - append('\n') - for (field in fields) { - appendIndented(this, field.toSchema()) - } - } - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/MessageElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/MessageElement.kt deleted file mode 100644 index e5ae8d9d..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/MessageElement.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class MessageElement( - override val location: Location, - override val name: String, - override val documentation: String = "", - override val nestedTypes: List = emptyList(), - override val options: List = emptyList(), - val reserveds: List = emptyList(), - val fields: List = emptyList(), - val oneOfs: List = emptyList(), - val extensions: List = emptyList(), - val groups: List = emptyList() -) : TypeElement { - override fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("message $name {") - - if (reserveds.isNotEmpty()) { - append('\n') - for (reserved in reserveds) { - appendIndented(this, reserved.toSchema()) - } - } - if (options.isNotEmpty()) { - append('\n') - for (option in options) { - appendIndented(this, option.toSchemaDeclaration()) - } - } - if (fields.isNotEmpty()) { - append('\n') - for (field in fields) { - appendIndented(this, field.toSchema()) - } - } - if (oneOfs.isNotEmpty()) { - append('\n') - for (oneOf in oneOfs) { - appendIndented(this, oneOf.toSchema()) - } - } - if (groups.isNotEmpty()) { - append('\n') - for (group in groups) { - appendIndented(this, group.toSchema()) - } - } - if (extensions.isNotEmpty()) { - append('\n') - for (extension in extensions) { - appendIndented(this, extension.toSchema()) - } - } - if (nestedTypes.isNotEmpty()) { - append('\n') - for (type in nestedTypes) { - appendIndented(this, type.toSchema()) - } - } - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/Nullable.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/Nullable.java deleted file mode 100644 index 7dbebd9e..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/Nullable.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import java.lang.annotation.Retention; - -@Retention(SOURCE) -public @interface Nullable { -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OneOfElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OneOfElement.kt deleted file mode 100644 index edf14ca1..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OneOfElement.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class OneOfElement( - val name: String, - val documentation: String = "", - val fields: List = emptyList(), - val groups: List = emptyList(), - val options: List = emptyList() - -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("oneof $name {") - if (options.isNotEmpty()) { - append('\n') - for (option in options) { - appendIndented(this, option.toSchema()); - } - } - - if (fields.isNotEmpty()) { - append('\n') - for (field in fields) { - appendIndented(this, field.toSchema()) - } - } - if (groups.isNotEmpty()) { - append('\n') - for (group in groups) { - appendIndented(this, group.toSchema()) - } - } - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionElement.kt deleted file mode 100644 index 9c489b6b..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionElement.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.Util.appendIndented -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.BOOLEAN -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.ENUM -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.LIST -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.MAP -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.NUMBER -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.OPTION -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.STRING - -data class OptionElement( - val name: String, - val kind: Kind, - val value: Any, - private val isParenthesized: Boolean -) { - enum class Kind { - STRING, - BOOLEAN, - NUMBER, - ENUM, - MAP, - LIST, - OPTION - } - - private val formattedName = if (isParenthesized) "($name)" else name - - fun toSchema(): String = buildString { - when (kind) { - STRING -> append("""$formattedName = "$value"""") - BOOLEAN, - NUMBER, - ENUM -> append("$formattedName = $value") - OPTION -> { - // Treat nested options as non-parenthesized always, prevents double parentheses. - val optionValue = (value as OptionElement).copy() - append("$formattedName.${optionValue.toSchema()}") - } - MAP -> { - append("$formattedName = {\n") - formatOptionMap(this, value as Map) - append('}') - } - LIST -> { - append("$formattedName = ") - Util.appendOptions(this, value as List) - } - } - } - - fun toSchemaDeclaration() = "option ${toSchema()};\n" - - private fun formatOptionMap( - builder: StringBuilder, - valueMap: Map - ) { - val lastIndex = valueMap.size - 1 - valueMap.entries.forEachIndexed { index, entry -> - val endl = if (index != lastIndex) "," else "" - appendIndented(builder, "${entry.key}: ${formatOptionMapValue(entry.value!!)}$endl") - } - } - - private fun formatOptionMapValue(value: Any): String = buildString { - when (value) { - is String -> { - append(""""$value"""") - } - is Map<*, *> -> { - append("{\n") - formatOptionMap(this, value as Map) - append('}') - } - is List<*> -> { - append("[\n") - val lastIndex = value.size - 1 - value.forEachIndexed { index, item -> - val endl = if (index != lastIndex) "," else "" - appendIndented(this, "${formatOptionMapValue(value[index]!!)}$endl") - } - append("]") - } - else -> { - append(value) - } - } - } - - companion object { - @JvmOverloads - fun create( - name: String, - kind: Kind, - value: Any, - isParenthesized: Boolean = false - ) = OptionElement(name, kind, value, isParenthesized) - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionReader.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionReader.kt deleted file mode 100644 index b8ac6dcf..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/OptionReader.kt +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2017 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.* -import java.util.* - -class OptionReader(internal val reader: SyntaxReader) { - - /** - * Reads options enclosed in '[' and ']' if they are present and returns them. Returns an empty - * list if no options are present. - */ - fun readOptions(): List { - if (!reader.peekChar('[')) return emptyList() - - val result = mutableListOf() - while (true) { - result += readOption('=') - - // Check for closing ']' - if (reader.peekChar(']')) break - - // Discard optional ','. - if (!reader.peekChar(',')) throw reader.unexpected("Expected ',' or ']") - } - return result - } - - /** Reads a option containing a name, an '=' or ':', and a value. */ - fun readOption(keyValueSeparator: Char): OptionElement { - val isExtension = reader.peekChar() == '[' - val isParenthesized = reader.peekChar() == '(' - var name = reader.readName() // Option name. - if (isExtension) name = "[$name]" - - var subName: String? = null - var c = reader.readChar() - if (c == '.') { - // Read nested field name. For example "baz" in "(foo.bar).baz = 12". - subName = reader.readName() - c = reader.readChar() - } - if (keyValueSeparator == ':' && c == '{') { - // In text format, values which are maps can omit a separator. Backtrack so it can be re-read. - reader.pushBack('{') - } else if (c != keyValueSeparator) { - throw reader.unexpected("expected '$keyValueSeparator' in option") - } - val kindAndValue = readKindAndValue() - var kind = kindAndValue.kind - var value = kindAndValue.value - if (subName != null) { - value = OptionElement.create(subName, kind, value) - kind = Kind.OPTION - } - return OptionElement.create(name, kind, value, isParenthesized) - } - - /** Reads a value that can be a map, list, string, number, boolean or enum. */ - private fun readKindAndValue(): KindAndValue { - val peeked = reader.peekChar() - when (peeked) { - '{' -> return KindAndValue(MAP, readMap('{', '}', ':')) - '[' -> return KindAndValue(LIST, readList()) - '"', '\'' -> return KindAndValue(STRING, reader.readString()) - else -> { - if (Character.isDigit(peeked) || peeked == '-') { - return KindAndValue(NUMBER, reader.readBigDecimal()) - } - val word = reader.readWord() - return when (word) { - "true" -> KindAndValue(BOOLEAN, true) - "false" -> KindAndValue(BOOLEAN, false) - else -> KindAndValue(ENUM, word) - } - } - } - } - - /** - * Returns a map of string keys and values. This is similar to a JSON object, with '{' and '}' - * surrounding the map, ':' separating keys from values, and ',' separating entries. - */ - private fun readMap( - openBrace: Char, - closeBrace: Char, - keyValueSeparator: Char - ): Map { - if (reader.readChar() != openBrace) throw AssertionError() - val result = mutableMapOf() - while (true) { - if (reader.peekChar(closeBrace)) { - // If we see the close brace, finish immediately. This handles {}/[] and ,}/,] cases. - return result - } - - val option = readOption(keyValueSeparator) - val name = option.name - val value = option.value - if (value is OptionElement) { - var nested = result[name] as? MutableMap - if (nested == null) { - nested = LinkedHashMap() - result[name] = nested - } - nested[value.name] = value.value - } else { - // Add the value(s) to any previous values with the same key - val previous = result[name] - when (previous) { - null -> result[name] = value - is List<*> -> // Add to previous List - addToList(previous as MutableList, value) - else -> { - val newList = ArrayList() - newList.add(previous) - addToList(newList, value) - result[name] = newList - } - } - } - - // Discard optional ',' separator. - reader.peekChar(',') - } - } - - /** Adds an object or objects to a List. */ - private fun addToList( - list: MutableList, - value: Any - ) { - if (value is List<*>) { - list.addAll(value as Collection) - } else { - list.add(value) - } - } - - /** - * Returns a list of values. This is similar to JSON with '[' and ']' surrounding the list and ',' - * separating values. - */ - private fun readList(): List { - reader.require('[') - val result = mutableListOf() - while (true) { - // If we see the close brace, finish immediately. This handles [] and ,] cases. - if (reader.peekChar(']')) return result - - result.add(readKindAndValue().value) - - if (reader.peekChar(',')) continue - if (reader.peekChar() != ']') throw reader.unexpected("expected ',' or ']'") - } - } - - internal data class KindAndValue( - internal val kind: Kind, - internal val value: Any - ) -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoFileElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoFileElement.kt deleted file mode 100644 index 3ac86afc..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoFileElement.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.ProtoFile - -/** A single `.proto` file. */ -data class ProtoFileElement( - val location: Location, - val packageName: String? = null, - val syntax: ProtoFile.Syntax? = null, - val imports: List = emptyList(), - val publicImports: List = emptyList(), - val types: List = emptyList(), - val services: List = emptyList(), - val extendDeclarations: List = emptyList(), - val options: List = emptyList() -) { - fun toSchema() = buildString { - append("// ") - append(location) - append('\n') - - if (syntax != null) { - append("syntax = \"$syntax\";\n") - } - if (packageName != null) { - append("package $packageName;\n") - } - if (imports.isNotEmpty() || publicImports.isNotEmpty()) { - append('\n') - for (file in imports) { - append("import \"$file\";\n") - } - for (file in publicImports) { - append("import public \"$file\";\n") - } - } - if (options.isNotEmpty()) { - append('\n') - for (option in options) { - append(option.toSchemaDeclaration()) - } - } - if (types.isNotEmpty()) { - append('\n') - for (typeElement in types) { - append(typeElement.toSchema()) - } - } - if (extendDeclarations.isNotEmpty()) { - append('\n') - for (extendDeclaration in extendDeclarations) { - append(extendDeclaration.toSchema()) - } - } - if (services.isNotEmpty()) { - append('\n') - for (service in services) { - append(service.toSchema()) - } - } - } -} \ No newline at end of file diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoParser.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoParser.java deleted file mode 100644 index b76e74fc..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ProtoParser.java +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Range; -import com.squareup.wire.schema.Field; -import com.squareup.wire.schema.Location; -import com.squareup.wire.schema.ProtoFile; -import com.squareup.wire.schema.internal.Util; - -/** Basic parser for {@code .proto} schema declarations. */ -public final class ProtoParser { - private final SyntaxReader reader; - private final Location location; - - /** Parse a named {@code .proto} schema. */ - public static ProtoFileElement parse(Location location, String data) { - return new ProtoParser(location, data.toCharArray()).readProtoFile(); - } - - private final ImmutableList.Builder publicImports = ImmutableList.builder(); - private final ImmutableList.Builder imports = ImmutableList.builder(); - private final ImmutableList.Builder nestedTypes = ImmutableList.builder(); - private final ImmutableList.Builder services = ImmutableList.builder(); - private final ImmutableList.Builder extendsList = ImmutableList.builder(); - private final ImmutableList.Builder options = ImmutableList.builder(); - - /** The number of declarations defined in the current file. */ - private int declarationCount = 0; - - /** The syntax of the file, or null if none is defined. */ - private ProtoFile.Syntax syntax; - - /** Output package name, or null if none yet encountered. */ - private String packageName; - - /** The current package name + nested type names, separated by dots. */ - private String prefix = ""; - - ProtoParser(Location location, char[] data) { - this.reader = new SyntaxReader(data, location); - this.location = location; - } - - ProtoFileElement readProtoFile() { - while (true) { - String documentation = reader.readDocumentation(); - if (reader.exhausted()) { - return new ProtoFileElement(location, packageName, syntax, imports.build(), publicImports.build(), nestedTypes.build(), services.build(), - extendsList.build(), options.build()); - } - Object declaration = readDeclaration(documentation, Context.FILE); - if (declaration instanceof TypeElement) { - nestedTypes.add((TypeElement) declaration); - } else if (declaration instanceof ServiceElement) { - services.add((ServiceElement) declaration); - } else if (declaration instanceof OptionElement) { - options.add((OptionElement) declaration); - } else if (declaration instanceof ExtendElement) { - extendsList.add((ExtendElement) declaration); - } - } - } - - private Object readDeclaration(String documentation, Context context) { - int index = declarationCount++; - - // Skip unnecessary semicolons, occasionally used after a nested message declaration. - if (reader.peekChar(';')) - return null; - - Location location = reader.location(); - String label = reader.readWord(); - - if (label.equals("package")) { - if (!context.permitsPackage()) - throw reader.unexpected(location, "'package' in " + context); - if (packageName != null) - throw reader.unexpected(location, "too many package names"); - packageName = reader.readName(); - prefix = packageName + "."; - reader.require(';'); - return null; - } else if (label.equals("import")) { - if (!context.permitsImport()) - throw reader.unexpected(location, "'import' in " + context); - String importString = reader.readString(); - if ("public".equals(importString)) { - publicImports.add(reader.readString()); - } else { - imports.add(importString); - } - reader.require(';'); - return null; - } else if (label.equals("syntax")) { - if (!context.permitsSyntax()) - throw reader.unexpected(location, "'syntax' in " + context); - reader.require('='); - if (index != 0) { - throw reader.unexpected(location, "'syntax' element must be the first declaration in a file"); - } - String syntaxString = reader.readQuotedString(); - try { - syntax = ProtoFile.Syntax.get(syntaxString); - } catch (IllegalArgumentException e) { - throw reader.unexpected(location, e.getMessage()); - } - reader.require(';'); - return null; - } else if (label.equals("option")) { - OptionElement result = new OptionReader(reader).readOption('='); - reader.require(';'); - return result; - } else if (label.equals("reserved")) { - return readReserved(location, documentation); - } else if (label.equals("message")) { - return readMessage(location, documentation); - } else if (label.equals("enum")) { - return readEnumElement(location, documentation); - } else if (label.equals("service")) { - return readService(location, documentation); - } else if (label.equals("extend")) { - return readExtend(location, documentation); - } else if (label.equals("rpc")) { - if (!context.permitsRpc()) - throw reader.unexpected(location, "'rpc' in " + context); - return readRpc(location, documentation); - } else if (label.equals("oneof")) { - if (!context.permitsOneOf()) { - throw reader.unexpected(location, "'oneof' must be nested in message"); - } - return readOneOf(documentation); - } else if (label.equals("extensions")) { - if (!context.permitsExtensions()) { - throw reader.unexpected(location, "'extensions' must be nested"); - } - return readExtensions(location, documentation); - } else if (context == Context.MESSAGE || context == Context.EXTEND) { - return readField(documentation, location, label); - } else if (context == Context.ENUM) { - return readEnumConstant(documentation, location, label); - } else { - throw reader.unexpected(location, "unexpected label: " + label); - } - } - - /** Reads a message declaration. */ - private MessageElement readMessage(Location location, String documentation) { - String name = reader.readName(); - - String previousPrefix = prefix; - prefix = prefix + name + "."; - - ImmutableList.Builder fields = ImmutableList.builder(); - ImmutableList.Builder oneOfs = ImmutableList.builder(); - ImmutableList.Builder nestedTypes = ImmutableList.builder(); - ImmutableList.Builder extensions = ImmutableList.builder(); - ImmutableList.Builder options = ImmutableList.builder(); - ImmutableList.Builder reserveds = ImmutableList.builder(); - ImmutableList.Builder groups = ImmutableList.builder(); - - reader.require('{'); - while (true) { - String nestedDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Object declared = readDeclaration(nestedDocumentation, Context.MESSAGE); - if (declared instanceof FieldElement) { - fields.add((FieldElement) declared); - } else if (declared instanceof OneOfElement) { - oneOfs.add((OneOfElement) declared); - } else if (declared instanceof GroupElement) { - groups.add((GroupElement) declared); - } else if (declared instanceof TypeElement) { - nestedTypes.add((TypeElement) declared); - } else if (declared instanceof ExtensionsElement) { - extensions.add((ExtensionsElement) declared); - } else if (declared instanceof OptionElement) { - options.add((OptionElement) declared); - } else if (declared instanceof ExtendElement) { - // Extend declarations always add in a global scope regardless of nesting. - extendsList.add((ExtendElement) declared); - } else if (declared instanceof ReservedElement) { - reserveds.add((ReservedElement) declared); - } - } - prefix = previousPrefix; - - return new MessageElement(location, name, documentation, nestedTypes.build(), options.build(), reserveds.build(), fields.build(), oneOfs.build(), - extensions.build(), groups.build()); - } - - /** Reads an extend declaration. */ - private ExtendElement readExtend(Location location, String documentation) { - String name = reader.readName(); - - reader.require('{'); - ImmutableList.Builder fields = ImmutableList.builder(); - while (true) { - String nestedDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Object declared = readDeclaration(nestedDocumentation, Context.EXTEND); - if (declared instanceof FieldElement) { - fields.add((FieldElement) declared); - } - } - return new ExtendElement(location, name, documentation, fields.build()); - } - - /** Reads a service declaration and returns it. */ - private ServiceElement readService(Location location, String documentation) { - String name = reader.readName(); - - reader.require('{'); - ImmutableList.Builder rpcs = ImmutableList.builder(); - ImmutableList.Builder options = ImmutableList.builder(); - while (true) { - String rpcDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Object declared = readDeclaration(rpcDocumentation, Context.SERVICE); - if (declared instanceof RpcElement) { - rpcs.add((RpcElement) declared); - } else if (declared instanceof OptionElement) { - options.add((OptionElement) declared); - } - } - return new ServiceElement(location, name, documentation, rpcs.build(), options.build()); - } - - /** Reads an enumerated type declaration and returns it. */ - private EnumElement readEnumElement(Location location, String documentation) { - String name = reader.readName(); - - ImmutableList.Builder constants = ImmutableList.builder(); - ImmutableList.Builder options = ImmutableList.builder(); - ImmutableList.Builder reserveds = ImmutableList.builder(); - - reader.require('{'); - while (true) { - String valueDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Object declared = readDeclaration(valueDocumentation, Context.ENUM); - if (declared instanceof EnumConstantElement) { - constants.add((EnumConstantElement) declared); - } else if (declared instanceof OptionElement) { - options.add((OptionElement) declared); - } else if (declared instanceof ReservedElement) { - reserveds.add((ReservedElement) declared); - } - } - return new EnumElement(location, name, documentation, options.build(), reserveds.build(), constants.build()); - } - - private Object readField(String documentation, Location location, String word) { - Field.Label label; - String type; - switch (word) { - case "required": - if (syntax == ProtoFile.Syntax.PROTO_3) { - throw reader.unexpected(location, "'required' label forbidden in proto3 field declarations"); - } - label = Field.Label.REQUIRED; - type = reader.readDataType(); - break; - - case "optional": - label = Field.Label.OPTIONAL; - type = reader.readDataType(); - break; - - case "repeated": - label = Field.Label.REPEATED; - type = reader.readDataType(); - break; - - default: - if (syntax != ProtoFile.Syntax.PROTO_3 && (!word.equals("map") || reader.peekChar() != '<')) { - throw reader.unexpected(location, "unexpected label: " + word); - } - label = null; - type = reader.readDataType(word); - break; - } - - if (type.startsWith("map<") && label != null) { - throw reader.unexpected(location, "'map' type cannot have label"); - } - if (type.equals("group")) { - return readGroup(location, documentation, label); - } - - return readField(location, documentation, label, type); - } - - /** Reads an field declaration and returns it. */ - private FieldElement readField(Location location, String documentation, @Nullable Field.Label label, String type) { - String name = reader.readName(); - reader.require('='); - int tag = reader.readInt(); - - List options = new OptionReader(reader).readOptions(); - reader.require(';'); - - options = new ArrayList<>(options); // Mutable copy for extractDefault. - String defaultValue = stripDefault(options); - - documentation = reader.tryAppendTrailingDocumentation(documentation); - return new FieldElement(location, label, type, name, defaultValue, tag, documentation, new ArrayList<>(options)); - } - - /** - * Defaults aren't options. This finds an option named "default", removes, and returns it. Returns null if no default option is present. - */ - private @Nullable String stripDefault(List options) { - String result = null; - for (Iterator i = options.iterator(); i.hasNext();) { - OptionElement option = i.next(); - if (option.getName().equals("default")) { - i.remove(); - result = String.valueOf(option.getValue()); // Defaults aren't options! - } - } - return result; - } - - private OneOfElement readOneOf(String documentation) { - String name = reader.readName(); - - ImmutableList.Builder fields = ImmutableList.builder(); - ImmutableList.Builder groups = ImmutableList.builder(); - ImmutableList.Builder options = ImmutableList.builder(); - - reader.require('{'); - while (true) { - String nestedDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Location location = reader.location(); - String type = reader.readDataType(); - if (type.equals("group")) { - groups.add(readGroup(location, nestedDocumentation, null)); - } else if (type.equals("option")) { - options.add(new OptionReader(reader).readOption('=')); - reader.require(';'); - } else { - fields.add(readField(location, nestedDocumentation, null, type)); - } - } - return new OneOfElement(name, documentation, fields.build(), groups.build(), options.build()); - } - - private GroupElement readGroup(Location location, String documentation, Field.Label label) { - String name = reader.readWord(); - reader.require('='); - int tag = reader.readInt(); - - ImmutableList.Builder fields = ImmutableList.builder(); - - reader.require('{'); - while (true) { - String nestedDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) - break; - - Location fieldLocation = reader.location(); - String fieldLabel = reader.readWord(); - Object field = readField(nestedDocumentation, fieldLocation, fieldLabel); - if (!(field instanceof FieldElement)) { - throw reader.unexpected("expected field declaration, was " + field); - } - fields.add((FieldElement) field); - } - - return new GroupElement(label, location, name, tag, documentation, fields.build()); - } - - /** Reads a reserved tags and names list like "reserved 10, 12 to 14, 'foo';". */ - private ReservedElement readReserved(Location location, String documentation) { - ImmutableList.Builder valuesBuilder = ImmutableList.builder(); - - while (true) { - char c = reader.peekChar(); - if (c == '"' || c == '\'') { - valuesBuilder.add(reader.readQuotedString()); - } else { - int tagStart = reader.readInt(); - - c = reader.peekChar(); - if (c != ',' && c != ';') { - if (!reader.readWord().equals("to")) { - throw reader.unexpected("expected ',', ';', or 'to'"); - } - int tagEnd; - if (reader.peekChar() == 'm') { - if (!reader.readWord().equals("max")) { - throw reader.unexpected("expected numeric identifier or 'max' keyword"); - } - tagEnd = Util.MAX_TAG_VALUE; - } else { - tagEnd = reader.readInt(); - } - valuesBuilder.add(Range.closed(tagStart, tagEnd)); - } else { - valuesBuilder.add(tagStart); - } - } - c = reader.readChar(); - if (c == ';') - break; - if (c != ',') - throw reader.unexpected("expected ',' or ';'"); - } - - ImmutableList values = valuesBuilder.build(); - if (values.isEmpty()) { - throw reader.unexpected("'reserved' must have at least one field name or tag"); - } - return new ReservedElement(location, documentation, values); - } - - /** Reads extensions like "extensions 101;", "extensions 101 to max;" or "extensions 101 [declaration = {...}];". */ - private ExtensionsElement readExtensions(Location location, String documentation) { - int start = reader.readInt(); // Range start. - int end = start; - char next = reader.peekChar(); - if (next != ';' && next != '[') { - if (!"to".equals(reader.readWord())) - throw reader.unexpected("expected ';', '[' or 'to'"); - String s = reader.readWord(); // Range end. - if (s.equals("max")) { - end = Util.MAX_TAG_VALUE; - } else { - end = Integer.parseInt(s); - } - } - // Extension ranges may carry options, e.g. "extensions 1000 to max [declaration = {...}];". These are - // descriptor-internal metadata that schema2proto does not re-emit, so parse and discard them. - new OptionReader(reader).readOptions(); - reader.require(';'); - return new ExtensionsElement(location, documentation, start, end); - } - - /** Reads an enum constant like "ROCK = 0;". The label is the constant name. */ - private EnumConstantElement readEnumConstant(String documentation, Location location, String label) { - reader.require('='); - - int tag = reader.readInt(); - - List options = new OptionReader(reader).readOptions(); - reader.require(';'); - documentation = reader.tryAppendTrailingDocumentation(documentation); - - return new EnumConstantElement(location, label, tag, documentation, options); - } - - /** Reads an rpc and returns it. */ - private RpcElement readRpc(Location location, String documentation) { - String name = reader.readName(); - - reader.require('('); - - boolean requestStreaming = false; - String requestType; - String word = reader.readWord(); - if (word.equals("stream")) { - requestStreaming = true; - requestType = reader.readDataType(); - } else { - requestType = reader.readDataType(word); - } - reader.require(')'); - - if (!reader.readWord().equals("returns")) - throw reader.unexpected("expected 'returns'"); - - reader.require('('); - - boolean responseStreaming = false; - String responseType; - word = reader.readWord(); - if (word.equals("stream")) { - responseStreaming = true; - responseType = reader.readDataType(); - } else { - responseType = reader.readDataType(word); - } - reader.require(')'); - - ImmutableList.Builder options = ImmutableList.builder(); - if (reader.peekChar('{')) { - while (true) { - String rpcDocumentation = reader.readDocumentation(); - if (reader.peekChar('}')) { - break; - } - Object declared = readDeclaration(rpcDocumentation, Context.RPC); - if (declared instanceof OptionElement) { - options.add((OptionElement) declared); - } - } - } else { - reader.require(';'); - } - - return new RpcElement(location, name, documentation, requestType, responseType, requestStreaming, responseStreaming, options.build()); - } - - enum Context { - FILE, - MESSAGE, - ENUM, - RPC, - EXTEND, - SERVICE; - - public boolean permitsPackage() { - return this == FILE; - } - - public boolean permitsSyntax() { - return this == FILE; - } - - public boolean permitsImport() { - return this == FILE; - } - - public boolean permitsExtensions() { - return this != FILE; - } - - public boolean permitsRpc() { - return this == SERVICE; - } - - public boolean permitsOneOf() { - return this == MESSAGE; - } - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ReservedElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ReservedElement.kt deleted file mode 100644 index c7948427..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ReservedElement.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema.internal.parser - -import com.google.common.collect.Range -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation - -data class ReservedElement( - val location: Location, - val documentation: String = "", - /** A [String] name or [Integer] or [Range][Range] tag. */ - val values: List -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("reserved ") - - val value = values - for (i in value.indices) { - if (i > 0) append(", ") - - val reservation = value[i] - when (reservation) { - is String -> append("\"$reservation\"") - is Int -> append(reservation) - is Range<*> -> { - val range = reservation as Range - append("${range.lowerEndpoint()} to ${range.upperEndpoint()}") - } - else -> throw AssertionError() - } - } - append(";\n") - } -} \ No newline at end of file diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/RpcElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/RpcElement.kt deleted file mode 100644 index 117c5cdb..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/RpcElement.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class RpcElement( - val location: Location, - val name: String, - val documentation: String = "", - val requestType: String, - val responseType: String, - val requestStreaming: Boolean = false, - val responseStreaming: Boolean = false, - val options: List = emptyList() -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("rpc $name (") - - if (requestStreaming) { - append("stream ") - } - append("$requestType) returns (") - - if (responseStreaming) { - append("stream ") - } - append("$responseType)") - - if (options.isNotEmpty()) { - append(" {\n") - for (option in options) { - appendIndented(this, option.toSchemaDeclaration()) - } - append('}') - } - - append(";\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ServiceElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ServiceElement.kt deleted file mode 100644 index 96954373..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/ServiceElement.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented - -data class ServiceElement( - val location: Location, - val name: String, - val documentation: String = "", - val rpcs: List = emptyList(), - val options: List = emptyList() -) { - fun toSchema() = buildString { - appendDocumentation(this, documentation) - append("service $name {") - if (options.isNotEmpty()) { - append('\n') - for (option in options) { - appendIndented(this, option.toSchemaDeclaration()) - } - } - if (rpcs.isNotEmpty()) { - append('\n') - for (rpc in rpcs) { - appendIndented(this, rpc.toSchema()) - } - } - append("}\n") - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/SyntaxReader.java b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/SyntaxReader.java deleted file mode 100644 index 58a781a3..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/SyntaxReader.java +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema.internal.parser; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -import java.math.BigDecimal; - -import com.squareup.wire.schema.Location; - -/** A general purpose reader for formats like {@code .proto}. */ -public final class SyntaxReader { - private final Location location; - - private final char[] data; - /** Our cursor within the document. {@code data[pos]} is the next character to be read. */ - private int pos; - /** The number of newline characters encountered thus far. */ - private int line; - /** The index of the most recent newline character. */ - private int lineStart; - - public SyntaxReader(char[] data, Location location) { - this.data = data; - this.location = location; - } - - public boolean exhausted() { - return pos == data.length; - } - - /** Reads a non-whitespace character and returns it. */ - public char readChar() { - char result = peekChar(); - pos++; - return result; - } - - /** Reads a non-whitespace character 'c', or throws an exception. */ - public void require(char c) { - if (readChar() != c) { - throw unexpected("expected '" + c + "'"); - } - } - - /** - * Peeks a non-whitespace character and returns it. The only difference between this and {@code readChar} is that this doesn't consume the char. - */ - public char peekChar() { - skipWhitespace(true); - if (pos == data.length) { - throw unexpected("unexpected end of file"); - } - return data[pos]; - } - - public boolean peekChar(char c) { - if (peekChar() == c) { - pos++; - return true; - } else { - return false; - } - } - - /** Push back the most recently read character. */ - public void pushBack(char c) { - if (data[pos - 1] != c) { - throw new IllegalArgumentException(); - } - pos--; - } - - /** Reads a quoted or unquoted string and returns it. */ - public String readString() { - skipWhitespace(true); - char c = peekChar(); - return c == '"' || c == '\'' ? readQuotedString() : readWord(); - } - - public String readQuotedString() { - char startQuote = readChar(); - if (startQuote != '"' && startQuote != '\'') { - throw new AssertionError(); - } - StringBuilder result = new StringBuilder(); - while (pos < data.length) { - char c = data[pos++]; - if (c == startQuote) { - if (peekChar() == '"' || peekChar() == '\'') { - // Adjacent strings are concatenated. Consume new quote and continue reading. - startQuote = readChar(); - continue; - } - return result.toString(); - } - - if (c == '\\') { - if (pos == data.length) { - throw unexpected("unexpected end of file"); - } - c = data[pos++]; - switch (c) { - case 'a': - c = 0x7; - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = 0xb; - break; - case 'x': - case 'X': - c = readNumericEscape(16, 2); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - --pos; - c = readNumericEscape(8, 3); - break; - default: - // use char as-is - result.append('\\'); - break; - } - } - - result.append(c); - if (c == '\n') { - newline(); - } - } - throw unexpected("unterminated string"); - } - - private char readNumericEscape(int radix, int len) { - int value = -1; - for (int endPos = Math.min(pos + len, data.length); pos < endPos; pos++) { - int digit = hexDigit(data[pos]); - if (digit == -1 || digit >= radix) { - break; - } - if (value < 0) { - value = digit; - } else { - value = value * radix + digit; - } - } - if (value < 0) { - throw unexpected("expected a digit after \\x or \\X"); - } - return (char) value; - } - - private int hexDigit(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - return c - 'A' + 10; - } else { - return -1; - } - } - - /** Reads a (paren-wrapped), [square-wrapped] or naked symbol name. */ - public String readName() { - String optionName; - char c = peekChar(); - if (c == '(') { - pos++; - optionName = readWord(); - if (readChar() != ')') { - throw unexpected("expected ')'"); - } - } else if (c == '[') { - pos++; - optionName = readWord(); - if (readChar() != ']') { - throw unexpected("expected ']'"); - } - } else { - optionName = readWord(); - } - return optionName; - } - - /** Reads a scalar, map, or type name. */ - public String readDataType() { - String name = readWord(); - return readDataType(name); - } - - /** Reads a scalar, map, or type name with {@code name} as a prefix word. */ - public String readDataType(String name) { - if (name.equals("map")) { - if (readChar() != '<') { - throw unexpected("expected '<'"); - } - String keyType = readDataType(); - if (readChar() != ',') { - throw unexpected("expected ','"); - } - String valueType = readDataType(); - if (readChar() != '>') { - throw unexpected("expected '>'"); - } - return String.format("map<%s, %s>", keyType, valueType); - } else { - return name; - } - } - - /** Reads a non-empty word and returns it. */ - public String readWord() { - skipWhitespace(true); - int start = pos; - while (pos < data.length) { - char c = data[pos]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '-') || (c == '.')) { - pos++; - } else { - break; - } - } - if (start == pos) { - throw unexpected("expected a word"); - } - return new String(data, start, pos - start); - } - - /** Reads an integer and returns it. */ - public int readInt() { - String tag = readWord(); - try { - int radix = 10; - if (tag.startsWith("0x") || tag.startsWith("0X")) { - tag = tag.substring("0x".length()); - radix = 16; - } - return Integer.valueOf(tag, radix); - } catch (Exception e) { - throw unexpected("expected an integer but was " + tag); - } - } - - /** Reads a long and returns it. */ - public BigDecimal readBigDecimal() { - String tag = readWord(); - try { - return new BigDecimal(tag); - } catch (Exception e) { - throw unexpected("expected a long but was " + tag); - } - } - - /** - * Like {@link #skipWhitespace}, but this returns a string containing all comment text. By convention, comments before a declaration document that - * declaration. - */ - public String readDocumentation() { - String result = null; - while (true) { - skipWhitespace(false); - if (pos == data.length || data[pos] != '/') { - return result != null ? result : ""; - } - String comment = readComment(); - result = (result == null) ? comment : (result + "\n" + comment); - } - } - - /** Reads a comment and returns its body. */ - private String readComment() { - if (pos == data.length || data[pos] != '/') { - throw new AssertionError(); - } - pos++; - int commentType = pos < data.length ? data[pos++] : -1; - if (commentType == '*') { - StringBuilder result = new StringBuilder(); - boolean startOfLine = true; - - for (; pos + 1 < data.length; pos++) { - char c = data[pos]; - if (c == '*' && data[pos + 1] == '/') { - pos += 2; - return result.toString().trim(); - } - if (c == '\n') { - result.append('\n'); - newline(); - startOfLine = true; - } else if (!startOfLine) { - result.append(c); - } else if (c == '*') { - if (data[pos + 1] == ' ') { - pos += 1; // Skip a single leading space, if present. - } - startOfLine = false; - } else if (!Character.isWhitespace(c)) { - result.append(c); - startOfLine = false; - } - } - throw unexpected("unterminated comment"); - } else if (commentType == '/') { - if (pos < data.length && data[pos] == ' ') { - pos += 1; // Skip a single leading space, if present. - } - int start = pos; - while (pos < data.length) { - char c = data[pos++]; - if (c == '\n') { - newline(); - break; - } - } - return new String(data, start, pos - 1 - start); - } else { - throw unexpected("unexpected '/'"); - } - } - - public String tryAppendTrailingDocumentation(String documentation) { - // Search for a '/' character ignoring spaces and tabs. - while (pos < data.length) { - char c = data[pos]; - if (c == ' ' || c == '\t') { - pos++; - } else if (c == '/') { - pos++; - break; - } else { - // Not a whitespace or comment-starting character. Return original documentation. - return documentation; - } - } - - if (pos == data.length || (data[pos] != '/' && data[pos] != '*')) { - pos--; // Backtrack to start of comment. - throw unexpected("expected '//' or '/*'"); - } - boolean isStar = data[pos] == '*'; - pos++; - - if (pos < data.length && data[pos] == ' ') { - pos++; // Skip a single leading space, if present. - } - - int start = pos; - int end; - - if (isStar) { - // Consume star comment until it closes on the same line. - while (true) { - if (pos == data.length) { - throw unexpected("trailing comment must be closed"); - } - if (data[pos] == '*' && pos + 1 < data.length && data[pos + 1] == '/') { - end = pos - 1; // The character before '*'. - pos += 2; // Skip to the character after '/'. - break; - } - pos++; - } - // Ensure nothing follows a trailing star comment. - while (pos < data.length) { - char c = data[pos++]; - if (c == '\n') { - newline(); - break; - } - if (c != ' ' && c != '\t') { - throw unexpected("no syntax may follow trailing comment"); - } - } - } else { - // Consume comment until newline. - while (true) { - if (pos == data.length) { - end = pos - 1; - break; - } - char c = data[pos++]; - if (c == '\n') { - newline(); - end = pos - 2; // Account for stepping past the newline. - break; - } - } - } - - // Remove trailing whitespace. - while (end > start && (data[end] == ' ' || data[end] == '\t')) { - end--; - } - - if (end == start) { - return documentation; - } - - String trailingDocumentation = new String(data, start, end - start + 1); - - return documentation.isEmpty() ? trailingDocumentation : documentation + '\n' + trailingDocumentation; - } - - /** - * Skips whitespace characters and optionally comments. When this returns, either {@code pos == data.length} or a non-whitespace character. - */ - private void skipWhitespace(boolean skipComments) { - while (pos < data.length) { - char c = data[pos]; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { - pos++; - if (c == '\n') { - newline(); - } - } else if (skipComments && c == '/') { - readComment(); - } else { - break; - } - } - } - - /** Call this every time a '\n' is encountered. */ - private void newline() { - line++; - lineStart = pos; - } - - public Location location() { - return location.at(line + 1, pos - lineStart + 1); - } - - public RuntimeException unexpected(String message) { - return unexpected(location(), message); - } - - public RuntimeException unexpected(Location location, String message) { - throw new IllegalStateException(String.format("Syntax error in %s: %s", location, message)); - } -} diff --git a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/TypeElement.kt b/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/TypeElement.kt deleted file mode 100644 index d04f68b5..00000000 --- a/schema2proto-wire/src/main/java/com/squareup/wire/schema/internal/parser/TypeElement.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location - -/** A message type or enum type declaration. */ -interface TypeElement { - val location: Location - val name: String - val documentation: String - val options: List - val nestedTypes: List - fun toSchema(): String -} diff --git a/schema2proto-wire/src/main/java/no/entur/schema2proto/JavadocClass.java b/schema2proto-wire/src/main/java/no/entur/schema2proto/JavadocClass.java deleted file mode 100644 index 278c1a3b..00000000 --- a/schema2proto-wire/src/main/java/no/entur/schema2proto/JavadocClass.java +++ /dev/null @@ -1,30 +0,0 @@ -package no.entur.schema2proto; - -/*- - * #%L - * schema2proto-wire - * %% - * Copyright (C) 2019 - 2020 Entur - * %% - * Licensed under the EUPL, Version 1.1 or – as soon they will be - * approved by the European Commission - subsequent versions of the - * EUPL (the "Licence"); - * - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * - * http://ec.europa.eu/idabc/eupl5 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * #L% - */ - -/** - * Javadoc jar for sonatype - */ -public class JavadocClass { -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/IdentifierSetTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/IdentifierSetTest.kt deleted file mode 100644 index 910008a1..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/IdentifierSetTest.kt +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import org.assertj.core.api.Assertions.assertThat - -import org.junit.jupiter.api.Test - -class IdentifierSetTest { - @Test - fun enclosing() { - assertThat(IdentifierSet.enclosing("a.b.Outer#member")).isEqualTo("a.b.Outer") - assertThat(IdentifierSet.enclosing("a.b.Outer")).isEqualTo("a.b.*") - assertThat(IdentifierSet.enclosing("a.b.*")).isEqualTo("a.*") - assertThat(IdentifierSet.enclosing("a.*")).isEqualTo("*") - assertThat(IdentifierSet.enclosing("*")).isNull() - } - - @Test - fun enclosingOnNestedClass() { - assertThat(IdentifierSet.enclosing("a.b.Outer.Inner#member")).isEqualTo("a.b.Outer.Inner") - assertThat(IdentifierSet.enclosing("a.b.Outer.Inner")).isEqualTo("a.b.Outer.*") - assertThat(IdentifierSet.enclosing("a.b.Outer.*")).isEqualTo("a.b.*") - } - - @Test - fun empty() { - val set = IdentifierSet.Builder().build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.INCLUDED) - } - - /** Note that including a type includes nested members, but not nested types. */ - @Test - fun includeType() { - val set = IdentifierSet.Builder() - .include("a.b.Message") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message.Nested")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun includeMember() { - val set = IdentifierSet.Builder() - .include("a.b.Message#member") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Message.Nested")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#other")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun includePackage() { - val set = IdentifierSet.Builder() - .include("a.b.*") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.c.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.c.Message#member")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.c.Another")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.c.Another#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun includeAll() { - val set = IdentifierSet.Builder() - .include("*") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.INCLUDED) - } - - @Test - fun excludeType() { - val set = IdentifierSet.Builder() - .exclude("a.b.Message") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.INCLUDED) - } - - @Test - fun excludeMember() { - val set = IdentifierSet.Builder() - .exclude("a.b.Message#member") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#other")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.INCLUDED) - } - - @Test - fun excludePackage() { - val set = IdentifierSet.Builder() - .exclude("a.b.*") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.c.Message")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.c.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.c.Another")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.c.Another#member")).isEqualTo(Policy.INCLUDED) - } - - @Test - fun excludePackageTakesPrecedenceOverIncludeType() { - val set = IdentifierSet.Builder() - .exclude("a.b.*") - .include("a.b.Message") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.c.YetAnother")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.c.YetAnother#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun excludeTypeTakesPrecedenceOverIncludeMember() { - val set = IdentifierSet.Builder() - .exclude("a.b.Message") - .include("a.b.Message#member") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#other")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun excludeMemberTakesPrecedenceOverIncludeType() { - val set = IdentifierSet.Builder() - .exclude("a.b.Message#member") - .include("a.b.Message") - .build() - assertThat(policy(set, "a.b.Message")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Message#member")).isEqualTo(Policy.EXCLUDED) - assertThat(policy(set, "a.b.Message#other")).isEqualTo(Policy.INCLUDED) - assertThat(policy(set, "a.b.Another")).isEqualTo(Policy.UNSPECIFIED) - assertThat(policy(set, "a.b.Another#member")).isEqualTo(Policy.UNSPECIFIED) - } - - @Test - fun trackingUnusedIncludes() { - val set = IdentifierSet.Builder() - .include("a.*") - .include("b.IncludedType") - .include("c.IncludedMember#member") - .build() - assertThat(set.unusedIncludes()).containsExactly("a.*", "b.IncludedType", - "c.IncludedMember#member") - - set.includes(ProtoType.get("a.*")) - assertThat(set.unusedIncludes()).containsExactly("b.IncludedType", "c.IncludedMember#member") - - set.includes(ProtoType.get("b.IncludedType")) - assertThat(set.unusedIncludes()).containsExactly("c.IncludedMember#member") - - set.includes(ProtoMember.get("c.IncludedMember#member")) - assertThat(set.unusedIncludes()).isEmpty() - } - - @Test - fun trackingUnusedExcludes() { - val set = IdentifierSet.Builder() - .exclude("a.*") - .exclude("b.ExcludedType") - .exclude("c.ExcludedMember#member") - .build() - assertThat(set.unusedExcludes()).containsExactly("a.*", "b.ExcludedType", - "c.ExcludedMember#member") - - set.includes(ProtoType.get("a.*")) - assertThat(set.unusedExcludes()).containsExactly("b.ExcludedType", "c.ExcludedMember#member") - - set.includes(ProtoType.get("b.ExcludedType")) - assertThat(set.unusedExcludes()).containsExactly("c.ExcludedMember#member") - - set.includes(ProtoMember.get("c.ExcludedMember#member")) - assertThat(set.unusedExcludes()).isEmpty() - } - - @Test - fun trackingUnusedIncludesPrecedence() { - val set = IdentifierSet.Builder() - .include("a.*") - .include("a.IncludedType") - .build() - set.includes(ProtoMember.get("a.IncludedType#member")) - assertThat(set.unusedIncludes()).containsExactly("a.IncludedType") - } - - @Test - fun trackingUnusedExcludesPrecedence() { - val set = IdentifierSet.Builder() - .exclude("a.*") - .exclude("a.IncludedType") - .build() - set.includes(ProtoMember.get("a.IncludedType#member")) - assertThat(set.unusedExcludes()).containsExactly("a.IncludedType") - } - - @Test - fun trackingUnusedPrecedence() { - val set = IdentifierSet.Builder() - .include("a.*") - .exclude("a.*") - .build() - set.includes(ProtoType.get("a.Message")) - assertThat(set.unusedExcludes()).isEmpty() - assertThat(set.unusedIncludes()).containsExactly("a.*") - } - - private fun policy(set: IdentifierSet, identifier: String): Policy { - return if (identifier.contains("#")) { - val protoMember = ProtoMember.get(identifier) - if (set.includes(protoMember)) return Policy.INCLUDED - if (set.excludes(protoMember)) Policy.EXCLUDED else Policy.UNSPECIFIED - } else { - val protoType = ProtoType.get(identifier) - if (set.includes(protoType)) return Policy.INCLUDED - if (set.excludes(protoType)) Policy.EXCLUDED else Policy.UNSPECIFIED - } - } - - internal enum class Policy { - INCLUDED, - UNSPECIFIED, - EXCLUDED - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/OptionsTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/OptionsTest.kt deleted file mode 100644 index 73b1b21d..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/OptionsTest.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class OptionsTest { - @Test - fun structuredAndUnstructuredOptions() { - // From https://developers.google.com/protocol-buffers/docs/proto#options - val schema = RepoBuilder() - .add("foo.proto", - """ - |import "google/protobuf/descriptor.proto"; - |message FooOptions { - | optional int32 opt1 = 1; - | optional string opt2 = 2; - |} - | - |extend google.protobuf.FieldOptions { - | optional FooOptions foo_options = 1234; - |} - | - |message Bar { - | optional int32 a = 1 [(foo_options).opt1 = 123, (foo_options).opt2 = "baz"]; - | optional int32 b = 2 [(foo_options) = { opt1: 456 opt2: "quux" }]; - | optional int32 c = 3 [(foo_options) = { opt2: "^([A-Z]{3}):([A-Za-z]+):([0-9ÆØÅæøåA-Za-z_\-]+)${'$'}" }]; - |} - """.trimMargin() - ) - .schema() - - val fooOptions = ProtoMember.get(Options.FIELD_OPTIONS, "foo_options") - val opt1 = ProtoMember.get(ProtoType.get("FooOptions"), "opt1") - val opt2 = ProtoMember.get(ProtoType.get("FooOptions"), "opt2") - - val bar = schema.getType("Bar") as MessageType - assertThat(bar.field("a")!!.options().map()) - .isEqualTo(mapOf(fooOptions to mapOf(opt1 to "123".toBigDecimal(), opt2 to "baz"))) - assertThat(bar.field("b")!!.options().map()) - .isEqualTo(mapOf(fooOptions to mapOf(opt1 to "456".toBigDecimal(), opt2 to "quux"))) - assertThat(bar.field("c")!!.options().map()) - .isEqualTo(mapOf(fooOptions to mapOf(opt2 to "^([A-Z]{3}):([A-Za-z]+):([0-9ÆØÅæøåA-Za-z_\\-]+)\$"))) - } - - @Test - fun textFormatCanOmitMapValueSeparator() { - val schema = RepoBuilder() - .add("foo.proto", - """ - |import "google/protobuf/descriptor.proto"; - |message FooOptions { - | optional BarOptions bar = 2; - |} - |message BarOptions { - | optional int32 baz = 2; - |} - | - |extend google.protobuf.FieldOptions { - | optional FooOptions foo = 1234; - |} - | - |message Message { - | optional int32 b = 2 [(foo) = { bar { baz: 123 } }]; - |} - """.trimMargin() - ) - .schema() - - val foo = ProtoMember.get(Options.FIELD_OPTIONS, "foo") - val bar = ProtoMember.get(ProtoType.get("FooOptions"), "bar") - val baz = ProtoMember.get(ProtoType.get("BarOptions"), "baz") - - val message = schema.getType("Message") as MessageType - assertThat(message.field("b")!!.options().map()) - .isEqualTo(mapOf(foo to mapOf(bar to mapOf(baz to "123".toBigDecimal())))) - } - - @Test - fun fullyQualifiedOptionFields() { - val schema = RepoBuilder() - .add("a/b/more_options.proto", - """ - |syntax = "proto2"; - |package a.b; - | - |import "google/protobuf/descriptor.proto"; - | - |extend google.protobuf.MessageOptions { - | optional MoreOptions more_options = 17000; - |} - | - |message MoreOptions { - | extensions 100 to 200; - |} - """.trimMargin() - ) - .add("a/c/event_more_options.proto", - """ - |syntax = "proto2"; - |package a.c; - | - |import "a/b/more_options.proto"; - | - |extend a.b.MoreOptions { - | optional EvenMoreOptions even_more_options = 100; - |} - | - |message EvenMoreOptions { - | optional string string_option = 1; - |} - """.trimMargin()) - .add("a/d/message.proto", - """ - |syntax = "proto2"; - |package a.d; - | - |import "a/b/more_options.proto"; - |import "a/c/event_more_options.proto"; - | - |message Message { - | option (a.b.more_options) = { - | [a.c.even_more_options]: {string_option: "foo"} - | }; - |} - """.trimMargin() - ) - .schema() - val moreOptionsType = ProtoType.get("a.b.MoreOptions") - val evenMoreOptionsType = ProtoType.get("a.c.EvenMoreOptions") - val moreOptions = ProtoMember.get(Options.MESSAGE_OPTIONS, "a.b.more_options") - val evenMoreOptions = ProtoMember.get(moreOptionsType, "a.c.even_more_options") - val stringOption = ProtoMember.get(evenMoreOptionsType, "string_option") - val message = schema.getType("a.d.Message") as MessageType - - assertThat(message.options().map()) - .isEqualTo(mapOf(moreOptions to mapOf(evenMoreOptions to mapOf(stringOption to "foo")))) - } - - @Test - fun resolveFieldPathMatchesFirstSegment() { - assertThat(Options.resolveFieldPath("a.b.c.d", setOf("a", "z", "y"))) - .containsExactly("a", "b", "c", "d") - } - - @Test - fun resolveFieldPathMatchesMultipleSegments() { - assertThat(Options.resolveFieldPath("a.b.c.d", setOf("a.b", "z.b", "y.b"))) - .containsExactly("a.b", "c", "d") - } - - @Test - fun resolveFieldPathMatchesAllSegments() { - assertThat(Options.resolveFieldPath("a.b.c.d", setOf("a.b.c.d", "z.b.c.d"))) - .containsExactly("a.b.c.d") - } - - @Test - fun resolveFieldPathMatchesOnlySegment() { - assertThat(Options.resolveFieldPath("a", setOf("a", "b"))).containsExactly("a") - } - - @Test - fun resolveFieldPathDoesntMatch() { - assertThat(Options.resolveFieldPath("a.b", setOf("c", "d"))).isNull() - } - - @Test - fun parseOneOfOptions() { - val schema = RepoBuilder().add("foo.proto", - """ - |import "google/protobuf/descriptor.proto"; - |message FooOptions { - | optional BarOptions bar = 2; - |} - |message BarOptions { - | optional int32 baz = 2; - | oneof enav { - | Code code = 1235; - |} - |} - |message Code{ - |optional bool defined_only = 2; - |} - |extend google.protobuf.OneofOptions { - | optional bool required = 1071; - |} - |extend google.protobuf.FieldOptions { - | optional FooOptions foo = 1234; - |} - | - |message Message { - |oneof a { - |option (required) = true; - |int32 b = 2 [(foo) = { bar { baz: 123 } }]; - |string c = 3 [(foo) = { bar { baz: 456 } }]; - |string d = 4 [(foo).bar.enav.code = {defined_only : true}]; - |} - |} - """.trimMargin() - ).schema() - - val foo = ProtoMember.get(Options.FIELD_OPTIONS, "foo") - val bar = ProtoMember.get(ProtoType.get("FooOptions"), "bar") - val baz = ProtoMember.get(ProtoType.get("BarOptions"), "baz") - val boo = ProtoMember.get(Options.ONE_OF_OPTIONS, "required") - - - val message = schema.getType("Message") as MessageType - assertThat(message.field("b")!!.options().map()) - .isEqualTo(mapOf(foo to mapOf(bar to mapOf(baz to "123".toBigDecimal())))) - - - val oneOf = message.oneOfs().get(0) - assertThat(oneOf.options().map()) - .isEqualTo(mapOf(boo to true)) - - assertThat(message.field("c")!!.options().map()) - .isEqualTo(mapOf(foo to mapOf(bar to mapOf(baz to "456".toBigDecimal())))) - - val chainedOptions = message.field("d")!!.options() - assertThat(chainedOptions.map()).isNotEmpty - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ParsingTester.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/ParsingTester.kt deleted file mode 100644 index eb6cf34b..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ParsingTester.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.internal.parser.ProtoParser -import java.io.IOException -import java.nio.file.* -import java.nio.file.attribute.BasicFileAttributes -import kotlin.jvm.Throws - -/** Recursively traverse a directory and attempt to parse all of its proto files. */ -object ParsingTester { - /** Directory under which to search for protos. Change as needed. */ - private val ROOT = Paths.get("/path/to/protos") - - @Throws(IOException::class) - @JvmStatic - fun main(args: Array) { - var total = 0L - var failed = 0L - - Files.walkFileTree(ROOT, object : SimpleFileVisitor() { - @Throws(IOException::class) - override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { - if (file.fileName.toString().endsWith(".proto")) { - total++ - - val data = file.toFile().readText() - val location = Location.get(ROOT.toString(), file.toString()) - try { - ProtoParser.parse(location, data) - } catch (e: Exception) { - e.printStackTrace() - failed++ - } - } - return FileVisitResult.CONTINUE - } - }) - - println("\nTotal: $total Failed: $failed") - - if (failed == 0L) { - SchemaLoader().addSource(ROOT).load() - println("All files linked successfully.") - } - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoFileTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoFileTest.kt deleted file mode 100644 index 941c9966..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoFileTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2016 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.internal.parser.ExtendElement -import com.squareup.wire.schema.internal.parser.FieldElement -import com.squareup.wire.schema.internal.parser.MessageElement -import com.squareup.wire.schema.internal.parser.OptionElement -import com.squareup.wire.schema.internal.parser.ProtoFileElement -import com.squareup.wire.schema.internal.parser.RpcElement -import com.squareup.wire.schema.internal.parser.ServiceElement -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class ProtoFileTest { - internal var location = Location.get("file.proto") - - @Test - fun roundTripToElement() { - val element1 = MessageElement( - location = location.at(11, 1), - name = "Message1", - documentation = "Some comments about Message1" - ) - val element2 = MessageElement( - location = location.at(12, 1), - name = "Message2", - fields = listOf( - FieldElement( - location = location.at(13, 3), - type = "string", - name = "field", - tag = 1 - ) - ) - ) - - val extend1 = ExtendElement( - location = location.at(16, 1), - name = "Extend1" - ) - val extend2 = ExtendElement( - location = location.at(17, 1), - name = "Extend2" - ) - val option1 = OptionElement.create("kit", OptionElement.Kind.STRING, "kat") - val option2 = OptionElement.create("foo", OptionElement.Kind.STRING, "bar") - val service1 = ServiceElement( - location = location.at(19, 1), - name = "Service1", - rpcs = listOf( - RpcElement( - location = location.at(20, 3), - name = "MethodA", - requestType = "Message2", - responseType = "Message1", - options = listOf( - OptionElement.create("methodoption", OptionElement.Kind.NUMBER, 1) - ) - ) - ) - ) - val service2 = ServiceElement( - location = location.at(24, 1), - name = "Service2" - ) - val fileElement = ProtoFileElement( - location = location, - packageName = "example.simple", - imports = listOf("example.thing"), - publicImports = listOf("example.other"), - types = listOf(element1, element2), - services = listOf(service1, service2), - extendDeclarations = listOf(extend1, extend2), - options = listOf(option1, option2) - ) - val file = ProtoFile.get(fileElement) - - val expected = """ - |// file.proto - |package example.simple; - | - |import "example.thing"; - |import public "example.other"; - | - |option kit = "kat"; - |option foo = "bar"; - | - |// Some comments about Message1 - |message Message1 {} - |message Message2 { - | string field = 1; - |} - | - |extend Extend1 {} - |extend Extend2 {} - | - |service Service1 { - | rpc MethodA (Message2) returns (Message1) { - | option methodoption = 1; - | }; - |} - |service Service2 {} - |""".trimMargin() - - assertThat(file.toSchema()).isEqualTo(expected) - assertThat(file.toElement()).isEqualToComparingFieldByField(fileElement) - } -} \ No newline at end of file diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoTypeTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoTypeTest.kt deleted file mode 100644 index 3e34e23a..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/ProtoTypeTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Assertions - - -class ProtoTypeTest { - @Test - fun get() { - assertThat(ProtoType.get("int32")).isSameAs(ProtoType.INT32) - assertThat(ProtoType.get("Person")).isEqualTo(ProtoType.get("Person")) - assertThat(ProtoType.get("squareup.protos.person", "Person")) - .isEqualTo(ProtoType.get("squareup.protos.person.Person")) - } - - @Test - fun simpleName() { - val person = ProtoType.get("squareup.protos.person.Person") - assertThat(person.simpleName()).isEqualTo("Person") - } - - @Test - fun scalarToString() { - assertThat(ProtoType.INT32.toString()).isEqualTo("int32") - assertThat(ProtoType.STRING.toString()).isEqualTo("string") - assertThat(ProtoType.BYTES.toString()).isEqualTo("bytes") - } - - @Test - fun nestedType() { - assertThat(ProtoType.get("squareup.protos.person.Person").nestedType("PhoneType")) - .isEqualTo(ProtoType.get("squareup.protos.person.Person.PhoneType")) - } - - @Test - fun primitivesCannotNest() { - try { - ProtoType.INT32.nestedType("PhoneType") - Assertions.assertTrue(false) - } catch (expected: UnsupportedOperationException) { - } - } - - @Test - fun mapsCannotNest() { - try { - ProtoType.get("map").nestedType("PhoneType") - Assertions.assertTrue(false) - } catch (expected: UnsupportedOperationException) { - } - } - - @Test - fun mapFormat() { - try { - ProtoType.get("map") - Assertions.assertTrue(false) - } catch (e: IllegalArgumentException) { - assertThat(e).hasMessage("expected ',' in map type: map") - } - } - - @Test - fun mapKeyScalarType() { - try { - ProtoType.get("map") - Assertions.assertTrue(false) - } catch (expected: IllegalArgumentException) { - } - - try { - ProtoType.get("map") - Assertions.assertTrue(false) - } catch (expected: IllegalArgumentException) { - } - - try { - ProtoType.get("map") - Assertions.assertTrue(false) - } catch (expected: IllegalArgumentException) { - } - - try { - ProtoType.get("map") - Assertions.assertTrue(false) - } catch (expected: IllegalArgumentException) { - } - } - - @Test - fun messageToString() { - val person = ProtoType.get("squareup.protos.person.Person") - assertThat(person.toString()).isEqualTo("squareup.protos.person.Person") - - val phoneType = person.nestedType("PhoneType") - assertThat(phoneType.toString()).isEqualTo("squareup.protos.person.Person.PhoneType") - } - - @Test - fun enclosingTypeOrPackage() { - assertThat(ProtoType.STRING.enclosingTypeOrPackage()).isNull() - - val person = ProtoType.get("squareup.protos.person.Person") - assertThat(person.enclosingTypeOrPackage()).isEqualTo("squareup.protos.person") - - val phoneType = person.nestedType("PhoneType") - assertThat(phoneType.enclosingTypeOrPackage()).isEqualTo("squareup.protos.person.Person") - } - - @Test - fun isScalar() { - assertThat(ProtoType.INT32.isScalar).isTrue() - assertThat(ProtoType.STRING.isScalar).isTrue() - assertThat(ProtoType.BYTES.isScalar).isTrue() - assertThat(ProtoType.get("squareup.protos.person.Person").isScalar).isFalse() - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/PrunerTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/PrunerTest.kt deleted file mode 100644 index da49ab33..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/PrunerTest.kt +++ /dev/null @@ -1,882 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.Options.FIELD_OPTIONS -import com.squareup.wire.schema.Options.MESSAGE_OPTIONS -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class PrunerTest { - @Test - fun retainType() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - |} - |message MessageB { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat(pruned.getType("MessageB")).isNull() - } - - @Test - fun retainMap() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | map maps = 1; - | message MessageB { - | } - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat(pruned.getField(ProtoMember.get("MessageA#maps"))).isNotNull - } - - @Test - fun excludeMap() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | map maps = 1; - | message MessageB { - | } - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .exclude("MessageA#maps") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat(pruned.getField(ProtoMember.get("MessageA#maps"))).isNull() - } - - @Test - fun retainTypeRetainsEnclosingButNotNested() { - val schema = RepoBuilder() - .add("service.proto", """ - |message A { - | message B { - | message C { - | } - | } - | message D { - | } - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("A.B") - .build()) - assertThat(pruned.getType("A")).isInstanceOf(EnclosingType::class.java) - assertThat(pruned.getType("A.B")).isInstanceOf(MessageType::class.java) - assertThat(pruned.getType("A.B.C")).isNull() - assertThat(pruned.getType("A.D")).isNull() - } - - @Test - fun retainTypeRetainsFieldTypesTransitively() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional MessageB b = 1; - |} - |message MessageB { - | map c = 1; - |} - |message MessageC { - |} - |message MessageD { - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat(pruned.getType("MessageB")).isNotNull - assertThat(pruned.getType("MessageC")).isNotNull - assertThat(pruned.getType("MessageD")).isNull() - } - - @Test - fun retainRpcRetainsRequestAndResponseTypes() { - val schema = RepoBuilder() - .add("service.proto", """ - |message RequestA { - |} - |message ResponseA { - |} - |message RequestB { - |} - |message ResponseB { - |} - |service Service { - | rpc CallA (RequestA) returns (ResponseA); - | rpc CallB (RequestB) returns (ResponseB); - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Service#CallA") - .build()) - assertThat(pruned.getService("Service").rpc("CallA")).isNotNull - assertThat(pruned.getType("RequestA")).isNotNull - assertThat(pruned.getType("ResponseA")).isNotNull - assertThat(pruned.getService("Service").rpc("CallB")).isNull() - assertThat(pruned.getType("RequestB")).isNull() - assertThat(pruned.getType("ResponseB")).isNull() - } - - @Test - fun retainField() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional string b = 1; - | map c = 2; - |} - |message MessageB { - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA#b") - .build()) - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("c")).isNull() - assertThat(pruned.getType("MessageB")).isNull() - } - - @Test - fun retainFieldRetainsFieldTypesTransitively() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional MessageB b = 1; - | optional MessageD d = 2; - |} - |message MessageB { - | optional MessageC c = 1; - |} - |message MessageC { - |} - |message MessageD { - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA#b") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("d")).isNull() - assertThat(pruned.getType("MessageB")).isNotNull - assertThat(pruned.getType("MessageC")).isNotNull - assertThat(pruned.getType("MessageD")).isNull() - } - - @Test - fun retainFieldPrunesOneOf() { - val schema = RepoBuilder() - .add("service.proto", """ - |message Message { - | oneof selection { - | string a = 1; - | string b = 2; - | } - | optional string c = 3; - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message#c") - .build()) - assertThat((pruned.getType("Message") as MessageType).oneOfs()).isEmpty() - } - - @Test - fun retainFieldRetainsOneOf() { - val schema = RepoBuilder() - .add("service.proto", """ - |message Message { - | oneof selection { - | string a = 1; - | string b = 2; - | } - | optional string c = 3; - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message#b") - .build()) - val message = pruned.getType("Message") as MessageType - val onlyOneOf = message.oneOfs().single() - assertThat(onlyOneOf.name()).isEqualTo("selection") - assertThat(onlyOneOf.fields().single().name()).isEqualTo("b") - assertThat(message.field("a")).isNull() - assertThat(message.field("c")).isNull() - } - - @Test - fun typeWithRetainedMembersOnlyHasThoseMembersRetained() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional MessageB b = 1; - |} - |message MessageB { - | optional MessageC c = 1; - | optional MessageD d = 2; - |} - |message MessageC { - |} - |message MessageD { - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA#b") - .include("MessageB#c") - .build()) - assertThat(pruned.getType("MessageA")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat(pruned.getType("MessageB")).isNotNull - assertThat((pruned.getType("MessageB") as MessageType).field("c")).isNotNull - assertThat((pruned.getType("MessageB") as MessageType).field("d")).isNull() - assertThat(pruned.getType("MessageC")).isNotNull - assertThat(pruned.getType("MessageD")).isNull() - } - - @Test - fun retainEnumConstant() { - val schema = RepoBuilder() - .add("service.proto", """ - |enum Roshambo { - | ROCK = 0; - | SCISSORS = 1; - | PAPER = 2; - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Roshambo#SCISSORS") - .build()) - assertThat((pruned.getType("Roshambo") as EnumType).constant("ROCK")).isNull() - assertThat((pruned.getType("Roshambo") as EnumType).constant("SCISSORS")).isNotNull - assertThat((pruned.getType("Roshambo") as EnumType).constant("PAPER")).isNull() - } - - @Test - fun enumWithRetainedConstantHasThatConstantRetained() { - val schema = RepoBuilder() - .add("service.proto", """ - |message Message { - | optional Roshambo roshambo = 1; - |} - |enum Roshambo { - | ROCK = 0; - | SCISSORS = 1; - | PAPER = 2; - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message") - .include("Roshambo#SCISSORS") - .build()) - assertThat(pruned.getType("Message")).isNotNull - assertThat((pruned.getType("Message") as MessageType).field("roshambo")).isNotNull - assertThat(pruned.getType("Roshambo")).isNotNull - assertThat((pruned.getType("Roshambo") as EnumType).constant("ROCK")).isNull() - assertThat((pruned.getType("Roshambo") as EnumType).constant("SCISSORS")).isNotNull - assertThat((pruned.getType("Roshambo") as EnumType).constant("PAPER")).isNull() - } - - @Test - fun retainedOptionRetainsOptionsType() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |extend google.protobuf.FieldOptions { - | optional string a = 22001; - |} - |message Message { - | optional string f = 1 [a = "a"]; - |} - """.trimMargin()) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message#f") - .build()) - assertThat((pruned.getType("Message") as MessageType).field("f")).isNotNull - assertThat(pruned.getType("google.protobuf.FieldOptions") as MessageType).isNotNull - } - - @Test - fun prunedOptionDoesNotRetainOptionsType() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |extend google.protobuf.FieldOptions { - | optional string a = 22001; - |} - |message Message { - | optional string f = 1 [a = "a"]; - | optional string g = 2; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message#g") - .build()) - assertThat(pruned.getType("google.protobuf.FieldOptions") as MessageType?).isNull() - } - - @Test - fun optionRetainsField() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |message SomeFieldOptions { - | optional string a = 1; // Retained via option use. - | optional string b = 2; // Retained explicitly. - | optional string c = 3; // Should be pruned. - |} - |extend google.protobuf.FieldOptions { - | optional SomeFieldOptions some_field_options = 22001; - |} - |message Message { - | optional string f = 1 [some_field_options.a = "a"]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message") - .include("SomeFieldOptions#b") - .build()) - assertThat((pruned.getType("Message") as MessageType).field("f")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("a")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("c")).isNull() - } - - @Test - fun optionRetainsType() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |message SomeFieldOptions { - | optional string a = 1; // Retained via option use. - | optional string b = 2; // Retained because 'a' is retained. - | optional string c = 3; // Retained because 'a' is retained. - |} - |extend google.protobuf.FieldOptions { - | optional SomeFieldOptions some_field_options = 22001; - |} - |message Message { - | optional string f = 1 [some_field_options.a = "a"]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message") - .build()) - assertThat((pruned.getType("Message") as MessageType).field("f")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("a")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("SomeFieldOptions") as MessageType).field("c")).isNotNull - } - - @Test - fun retainExtension() { - val schema = RepoBuilder() - .add("service.proto", """ - |message Message { - | optional string a = 1; - |} - |extend Message { - | optional string b = 2; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message") - .build()) - assertThat((pruned.getType("Message") as MessageType).field("a")).isNotNull - assertThat((pruned.getType("Message") as MessageType).extensionField("b")).isNotNull - } - - @Test - fun retainExtensionMembers() { - val schema = RepoBuilder() - .add("service.proto", """ - |message Message { - | optional string a = 1; - | optional string b = 2; - |} - |extend Message { - | optional string c = 3; - | optional string d = 4; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Message#a") - .include("Message#c") - .build()) - assertThat((pruned.getType("Message") as MessageType).field("a")).isNotNull - assertThat((pruned.getType("Message") as MessageType).field("b")).isNull() - assertThat((pruned.getType("Message") as MessageType).extensionField("c")).isNotNull - assertThat((pruned.getType("Message") as MessageType).extensionField("d")).isNull() - } - - /** When we include excludes only, the mark phase is skipped. */ - @Test - fun excludeWithoutInclude() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional string b = 1; - | optional string c = 2; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("MessageA#c") - .build()) - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("c")).isNull() - } - - @Test - fun excludeField() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional string b = 1; - | optional string c = 2; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .exclude("MessageA#c") - .build()) - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("c")).isNull() - } - - @Test - fun excludeTypeExcludesField() { - val schema = RepoBuilder() - .add("service.proto", """ - |message MessageA { - | optional MessageB b = 1; - | map c = 2; - |} - |message MessageB { - |} - |message MessageC { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .exclude("MessageC") - .build()) - assertThat(pruned.getType("MessageB")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat(pruned.getType("MessageC")).isNull() - assertThat((pruned.getType("MessageA") as MessageType).field("c")).isNull() - } - - @Test - fun excludeTypeExcludesRpc() { - val schema = RepoBuilder() - .add("service.proto", """ - |service ServiceA { - | rpc CallB (MessageB) returns (MessageB); - | rpc CallC (MessageC) returns (MessageC); - |} - |message MessageB { - |} - |message MessageC { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("ServiceA") - .exclude("MessageC") - .build()) - assertThat(pruned.getType("MessageB")).isNotNull - assertThat(pruned.getService("ServiceA").rpc("CallB")).isNotNull - assertThat(pruned.getType("MessageC")).isNull() - assertThat(pruned.getService("ServiceA").rpc("CallC")).isNull() - } - - @Test - fun excludeRpcExcludesTypes() { - val schema = RepoBuilder() - .add("service.proto", """ - |service ServiceA { - | rpc CallB (MessageB) returns (MessageB); - | rpc CallC (MessageC) returns (MessageC); - |} - |message MessageB { - |} - |message MessageC { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("ServiceA") - .exclude("ServiceA#CallC") - .build()) - assertThat(pruned.getType("MessageB")).isNotNull - assertThat(pruned.getService("ServiceA").rpc("CallB")).isNotNull - assertThat(pruned.getType("MessageC")).isNull() - assertThat(pruned.getService("ServiceA").rpc("CallC")).isNull() - } - - @Test - fun excludeFieldExcludesTypes() { - val schema = RepoBuilder() - .add("message.proto", """ - |message MessageA { - | optional MessageB b = 1; - | optional MessageC c = 2; - | map d = 3; - |} - |message MessageB { - |} - |message MessageC { - |} - |message MessageD { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("MessageA") - .exclude("MessageA#c") - .exclude("MessageA#d") - .build()) - assertThat((pruned.getType("MessageA") as MessageType).field("b")).isNotNull - assertThat(pruned.getType("MessageB")).isNotNull - assertThat((pruned.getType("MessageA") as MessageType).field("c")).isNull() - assertThat(pruned.getType("MessageC")).isNull() - assertThat((pruned.getType("MessageA") as MessageType).field("d")).isNull() - assertThat(pruned.getType("MessageD")).isNull() - } - - @Test - fun excludeEnumExcludesOptions() { - val schema = RepoBuilder() - .add("message.proto", """ - |import "google/protobuf/descriptor.proto"; - |enum Enum { - | A = 0; - | B = 1 [message.c = 1]; - |} - |extend google.protobuf.EnumValueOptions { - | optional Message message = 70000; - |}; - |message Message { - | optional int32 c = 1; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("Enum") - .exclude("Enum#B") - .build()) - assertThat((pruned.getType("Enum") as EnumType).constant("A")).isNotNull - assertThat((pruned.getType("Enum") as EnumType).constant("B")).isNull() - assertThat(pruned.getType("Message")).isNull() - } - - @Test - fun excludedFieldPrunesTopLevelOption() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |extend google.protobuf.FieldOptions { - | optional string a = 22001; - | optional string b = 22002; - |} - |message Message { - | optional string f = 1 [a = "a", b = "b"]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("google.protobuf.FieldOptions#b") - .build()) - val field = (pruned.getType("Message") as MessageType).field("f")!! - assertThat(field.options().get(ProtoMember.get(FIELD_OPTIONS, "a"))).isEqualTo("a") - assertThat(field.options().get(ProtoMember.get(FIELD_OPTIONS, "b"))).isNull() - } - - @Test - fun excludedTypePrunesTopLevelOption() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |message SomeFieldOptions { - | optional string a = 1; - |} - |extend google.protobuf.FieldOptions { - | optional SomeFieldOptions some_field_options = 22001; - | optional string b = 22002; - |} - |message Message { - | optional string f = 1 [some_field_options.a = "a", b = "b"]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("SomeFieldOptions") - .build()) - val field = (pruned.getType("Message") as MessageType).field("f") - val map = field!!.options().map() - val onlyOption = map.entries.single() - assertThat((onlyOption.key as ProtoMember).member()).isEqualTo("b") - assertThat(onlyOption.value).isEqualTo("b") - } - - @Test - fun excludedFieldPrunesNestedOption() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |message SomeFieldOptions { - | optional string a = 1; - | optional string b = 2; - |} - |extend google.protobuf.FieldOptions { - | optional SomeFieldOptions some_field_options = 22001; - |} - |message Message { - | optional string f = 1 [some_field_options = { a: "a", b: "b" }]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("SomeFieldOptions#b") - .build()) - val field = (pruned.getType("Message") as MessageType).field("f") - val map = field!!.options().get( - ProtoMember.get(FIELD_OPTIONS, "some_field_options")) as Map<*, *> - val onlyOption = map.entries.single() - assertThat((onlyOption.key as ProtoMember).member()).isEqualTo("a") - assertThat(onlyOption.value).isEqualTo("a") - } - - @Test - fun excludedTypePrunesNestedOption() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |message SomeFieldOptions { - | optional Dimensions dimensions = 1; - |} - |message Dimensions { - | optional string length = 1; - | optional string width = 2; - |} - |extend google.protobuf.FieldOptions { - | optional SomeFieldOptions some_field_options = 22001; - | optional string b = 22002; - |} - |message Message { - | optional string f = 1 [ - | some_field_options = { - | dimensions: { length: "100" } - | }, - | b = "b" - | ]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("Dimensions") - .build()) - val field = (pruned.getType("Message") as MessageType).field("f") - val map = field!!.options().map() - val onlyOption = map.entries.single() - assertThat((onlyOption.key as ProtoMember).member()).isEqualTo("b") - assertThat(onlyOption.value).isEqualTo("b") - } - - @Test - fun excludeOptions() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |extend google.protobuf.FieldOptions { - | optional string a = 22001; - | optional string b = 22002; - |} - |message Message { - | optional string f = 1 [ a = "a", b = "b" ]; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("google.protobuf.FieldOptions") - .build()) - val field = (pruned.getType("Message") as MessageType).field("f") - assertThat(field!!.options().map()).isEmpty() - } - - @Test - fun excludeRepeatedOptions() { - val schema = RepoBuilder() - .add("service.proto", """ - |import "google/protobuf/descriptor.proto"; - |extend google.protobuf.MessageOptions { - | repeated string a = 22001; - | repeated string b = 22002; - |} - |message Message { - | option (a) = "a1"; - | option (a) = "a2"; - | option (b) = "b1"; - | option (b) = "b2"; - | optional string f = 1; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("google.protobuf.MessageOptions#a") - .build()) - val message = pruned.getType("Message") as MessageType - assertThat(message.options().get(ProtoMember.get(MESSAGE_OPTIONS, "a"))).isNull() - assertThat(message.options().get(ProtoMember.get(MESSAGE_OPTIONS, "b"))) - .isEqualTo(listOf("b1", "b2")) - } - - @Test - fun includePackage() { - val schema = RepoBuilder() - .add("a/b/messages.proto", """ - |package a.b; - |message MessageAB { - |} - """.trimMargin() - ) - .add("a/c/messages.proto", """ - |package a.c; - |message MessageAC { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .include("a.b.*") - .build()) - assertThat(pruned.getType("a.b.MessageAB")).isNotNull - assertThat(pruned.getType("a.c.MessageAC")).isNull() - } - - @Test - fun excludePackage() { - val schema = RepoBuilder() - .add("a/b/messages.proto", """ - |package a.b; - |message MessageAB { - |} - """.trimMargin() - ) - .add("a/c/messages.proto", """ - |package a.c; - |message MessageAC { - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("a.c.*") - .build()) - assertThat(pruned.getType("a.b.MessageAB")).isNotNull - assertThat(pruned.getType("a.c.MessageAC")).isNull() - } - - @Test - fun specialOptionsNotPruned() { - val schema = RepoBuilder() - .add("message.proto", """ - |option java_package = "p"; - | - |message Message { - | optional int32 a = 1 [packed = true, deprecated = true, default = 5]; - |} - |enum Enum { - | option allow_alias = true; - | A = 1; - | B = 1; - |} - """.trimMargin() - ) - .schema() - val pruned = schema.prune(IdentifierSet.Builder() - .exclude("google.protobuf.*") - .build()) - val protoFile = pruned.protoFile("message.proto") - assertThat(protoFile!!.javaPackage()).isEqualTo("p") - - val message = pruned.getType("Message") as MessageType - val field = message.field("a") - assertThat(field!!.default).isEqualTo("5") - assertThat(field.isDeprecated).isTrue() - assertThat(field.isPacked).isTrue() - - val enumType = pruned.getType("Enum") as EnumType - assertThat(enumType.allowAlias()).isTrue() - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/RepoBuilder.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/RepoBuilder.kt deleted file mode 100644 index 87a16973..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/RepoBuilder.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.google.common.jimfs.Configuration -import com.google.common.jimfs.Jimfs -import java.io.IOException -import java.nio.charset.StandardCharsets.UTF_8 -import java.nio.file.Files - -/** - * Builds a repository of `.proto` and `.wire` files to create schemas, profiles, and adapters for - * testing. - */ -class RepoBuilder { - private val fs = Jimfs.newFileSystem(Configuration.unix()) - private val root = fs.getPath("/source") - private val schemaLoader = SchemaLoader().addSource(root) - - fun add(name: String, protoFile: String): RepoBuilder { - if (name.endsWith(".proto")) { - schemaLoader.addProto(name) - } else if (!name.endsWith(".wire")) { - throw IllegalArgumentException("unexpected file extension: $name") - } - - val relativePath = fs.getPath(name) - try { - val resolvedPath = root.resolve(relativePath) - val parent = resolvedPath.parent - if (parent != null) { - Files.createDirectories(parent) - } - Files.write(resolvedPath, protoFile.toByteArray(UTF_8)) - } catch (e: IOException) { - throw AssertionError(e) - } - - return this - } - -/* @Throws(IOException::class) - fun add(path: String): RepoBuilder { - val file = File("../wire-tests/src/test/proto/$path") - file.source().use { source -> - val protoFile = source.buffer().readUtf8() - return add(path, protoFile) - } - }*/ - - fun schema(): Schema { - try { - return schemaLoader.load() - } catch (e: IOException) { - throw RuntimeException(e) - } - - } - - -} \ No newline at end of file diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaLoaderTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaLoaderTest.kt deleted file mode 100644 index bbb35780..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaLoaderTest.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - - -import com.google.common.jimfs.Configuration -import com.google.common.jimfs.Jimfs -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import java.io.FileNotFoundException -import java.io.IOException -import java.nio.charset.StandardCharsets.UTF_8 -import java.nio.file.Files -import java.nio.file.Path -import java.util.zip.ZipEntry -import java.util.zip.ZipOutputStream -import kotlin.jvm.Throws - -class SchemaLoaderTest { - private var fileSystem = Jimfs.newFileSystem(Configuration.unix()) - - @Test - @Throws(IOException::class) - fun loadAllFilesWhenNoneSpecified() { - Files.createDirectories(fileSystem.getPath("/source")) - - fileSystem.getPath("/source/message1.proto").writeText("message Message1 {}") - fileSystem.getPath("/source/message2.proto").writeText("message Message2 {}") - fileSystem.getPath("/source/readme.txt").writeText("Here be protos!") - - val schema = SchemaLoader() - .addSource(fileSystem.getPath("/source")) - .load() - - val message1 = schema.getType("Message1") - assertThat(message1).isNotNull - assertThat(message1.location().base).isEqualTo("/source") - assertThat(message1.location().path).isEqualTo("message1.proto") - - val message2 = schema.getType("Message2") - assertThat(message2).isNotNull - assertThat(message2.location().base).isEqualTo("/source") - assertThat(message2.location().path).isEqualTo("message2.proto") - } - - @Test - @Throws(IOException::class) - fun locateInMultiplePaths() { - Files.createDirectories(fileSystem.getPath("/source1")) - Files.createDirectories(fileSystem.getPath("/source2")) - Files.createFile(fileSystem.getPath("/source1/file1.proto")) - Files.createFile(fileSystem.getPath("/source2/file2.proto")) - - SchemaLoader() - .addSource(fileSystem.getPath("/source1")) - .addSource(fileSystem.getPath("/source2")) - .addProto("file1.proto") - .addProto("file2.proto") - .load() - } - - @Test - @Throws(IOException::class) - fun failLocate() { - Files.createDirectories(fileSystem.getPath("/source1")) - Files.createDirectories(fileSystem.getPath("/source2")) - Files.createFile(fileSystem.getPath("/source2/file2.proto")) - - val loader = SchemaLoader() - .addSource(fileSystem.getPath("/source1")) - .addProto("file2.proto") - try { - loader.load() - Assertions.assertTrue(false) - } catch (expected: FileNotFoundException) { - } - } - - @Test - @Throws(IOException::class) - fun locateInZipFile() { - Files.createDirectories(fileSystem.getPath("/source")) - val zip = fileSystem.getPath("/source/protos.zip") - val zipOutputStream = ZipOutputStream(Files.newOutputStream(zip)) - zipOutputStream.putNextEntry(ZipEntry("a/b/message.proto")) - zipOutputStream.write("message Message {}".toByteArray(UTF_8)) - zipOutputStream.close() - - val schema = SchemaLoader() - .addSource(zip) - .addProto("a/b/message.proto") - .load() - - val message = schema.getType("Message") - assertThat(message).isNotNull - assertThat(message.location().base).isEqualTo("/source/protos.zip") - assertThat(message.location().path).isEqualTo("a/b/message.proto") - } - - @Test - @Throws(IOException::class) - fun failLocateInZipFile() { - Files.createDirectories(fileSystem.getPath("/source")) - val zip = fileSystem.getPath("/source/protos.zip") - val zipOutputStream = ZipOutputStream(Files.newOutputStream(zip)) - zipOutputStream.putNextEntry(ZipEntry("a/b/trix.proto")) - zipOutputStream.write("message Trix {}".toByteArray(UTF_8)) - zipOutputStream.close() - - try { - SchemaLoader() - .addSource(zip) - .addProto("a/b/message.proto") - .load() - Assertions.assertTrue(false) - } catch (expected: FileNotFoundException) { - } - } - - @Test - @Throws(IOException::class) - fun earlierSourcesTakePrecedenceOverLaterSources() { - Files.createDirectories(fileSystem.getPath("/source1")) - Files.createDirectories(fileSystem.getPath("/source2")) - - fileSystem.getPath("/source1/message.proto") - .writeText(""" - |message Message { - | optional string a = 1; - |} - """.trimMargin()) - - fileSystem.getPath("/source2/message.proto") - .writeText(""" - |message Message { - | optional string b = 2; - |} - """.trimMargin()) - - val schema = SchemaLoader() - .addSource(fileSystem.getPath("/source1")) - .addSource(fileSystem.getPath("/source2")) - .load() - val message = schema.getType("Message") as MessageType - assertThat(message.field("a")).isNotNull - } - - @Throws(IOException::class) - private fun Path.writeText(content: String) { - Files.write(this, content.toByteArray(UTF_8)) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaTest.kt deleted file mode 100644 index a784fdfe..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/SchemaTest.kt +++ /dev/null @@ -1,1536 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema - -import com.squareup.wire.schema.Options.FIELD_OPTIONS -import com.squareup.wire.schema.internal.Util -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - - -class SchemaTest { - @Test - fun linkService() { - val schema = RepoBuilder() - .add("service.proto", - """ - |import "request.proto"; - |import "response.proto"; - |service Service { - | rpc Call (Request) returns (Response); - |} - """.trimMargin() - ) - .add("request.proto", - """ - |message Request { - |} - """.trimMargin() - ) - .add("response.proto", - """ - |message Response { - |} - """.trimMargin() - ) - .schema() - - val service = schema.getService("Service") - val call = service.rpc("Call")!! - assertThat(call.requestType()).isEqualTo(schema.getType("Request").type()) - assertThat(call.responseType()).isEqualTo(schema.getType("Response").type()) - } - - @Test - fun linkMessage() { - val schema = RepoBuilder() - .add("message.proto", - """ - |import "foo.proto"; - |message Message { - | optional foo_package.Foo field = 1; - | map bars = 2; - |} - """.trimMargin() - ) - .add("foo.proto", - """ - |package foo_package; - |message Foo { - |} - |message Bar { - |} - """.trimMargin() - ) - .schema() - - val message = schema.getType("Message") as MessageType - val field = message.field("field") - assertThat(field!!.type()).isEqualTo(schema.getType("foo_package.Foo").type()) - val bars = message.field("bars")!!.type() - assertThat(bars.keyType()).isEqualTo(ProtoType.STRING) - assertThat(bars.valueType()).isEqualTo(schema.getType("foo_package.Bar").type()) - } - - - @Test - fun isValidTag() { - assertThat(Util.isValidTag(0)).isFalse() // Less than minimum. - assertThat(Util.isValidTag(1)).isTrue() - assertThat(Util.isValidTag(1234)).isTrue() - assertThat(Util.isValidTag(19222)).isFalse() // Reserved range. - assertThat(Util.isValidTag(2319573)).isTrue() - assertThat(Util.isValidTag(536870911)).isTrue() - assertThat(Util.isValidTag(536870912)).isFalse() // Greater than maximum. - } - - @Test - fun fieldInvalidTag() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | optional int32 a = 0; - | optional int32 b = 1; - | optional int32 c = 18999; - | optional int32 d = 19000; - | optional int32 e = 19999; - | optional int32 f = 20000; - | optional int32 g = 536870911; - | optional int32 h = 536870912; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |tag is out of range: 0 - | for field a (/source/message.proto at 2:3) - | in message Message (/source/message.proto at 1:1) - |tag is out of range: 19000 - | for field d (/source/message.proto at 5:3) - | in message Message (/source/message.proto at 1:1) - |tag is out of range: 19999 - | for field e (/source/message.proto at 6:3) - | in message Message (/source/message.proto at 1:1) - |tag is out of range: 536870912 - | for field h (/source/message.proto at 9:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin()) - } - } - - @Test - fun extensionsInvalidTag() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | extensions 0; - | extensions 1; - | extensions 18999; - | extensions 19000; - | extensions 19999; - | extensions 20000; - | extensions 536870911; - | extensions 536870912; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |tags are out of range: 0 to 0 - | for extensions (/source/message.proto at 2:3) - | in message Message (/source/message.proto at 1:1) - |tags are out of range: 19000 to 19000 - | for extensions (/source/message.proto at 5:3) - | in message Message (/source/message.proto at 1:1) - |tags are out of range: 19999 to 19999 - | for extensions (/source/message.proto at 6:3) - | in message Message (/source/message.proto at 1:1) - |tags are out of range: 536870912 to 536870912 - | for extensions (/source/message.proto at 9:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun scalarFieldIsPacked() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - | repeated int32 a = 1; - | repeated int32 b = 2 [packed=false]; - | repeated int32 c = 3 [packed=true]; - |} - """.trimMargin() - ) - .schema() - - val message = schema.getType("Message") as MessageType - assertThat(message.field("a")!!.isPacked).isFalse() - assertThat(message.field("b")!!.isPacked).isFalse() - assertThat(message.field("c")!!.isPacked).isTrue() - } - - @Test - fun enumFieldIsPacked() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - | repeated HabitablePlanet home_planet = 1 [packed=true]; - | enum HabitablePlanet { - | EARTH = 1; - | } - |} - """.trimMargin() - ) - .schema() - val message = schema.getType("Message") as MessageType - assertThat(message.field("home_planet")!!.isPacked).isTrue() - } - - @Test - fun fieldIsPackedButShouldntBe() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | repeated bytes a = 1 [packed=false]; - | repeated bytes b = 2 [packed=true]; - | repeated string c = 3 [packed=false]; - | repeated string d = 4 [packed=true]; - | repeated Message e = 5 [packed=false]; - | repeated Message f = 6 [packed=true]; - |} - |extend Message { - | repeated bytes g = 7 [packed=false]; - | repeated bytes h = 8 [packed=true]; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |packed=true not permitted on bytes - | for field b (/source/message.proto at 3:3) - | in message Message (/source/message.proto at 1:1) - |packed=true not permitted on string - | for field d (/source/message.proto at 5:3) - | in message Message (/source/message.proto at 1:1) - |packed=true not permitted on Message - | for field f (/source/message.proto at 7:3) - | in message Message (/source/message.proto at 1:1) - |packed=true not permitted on bytes - | for field h (/source/message.proto at 11:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun fieldIsDeprecated() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - | optional int32 a = 1; - | optional int32 b = 2 [deprecated=false]; - | optional int32 c = 3 [deprecated=true]; - |} - """.trimMargin() - ) - .schema() - - val message = schema.getType("Message") as MessageType - assertThat(message.field("a")!!.isDeprecated).isFalse() - assertThat(message.field("b")!!.isDeprecated).isFalse() - assertThat(message.field("c")!!.isDeprecated).isTrue() - } - - @Test - fun fieldDefault() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - | optional int32 a = 1; - | optional int32 b = 2 [default = 5]; - | optional bool c = 3 [default = true]; - | optional string d = 4 [default = "foo"]; - | optional Roshambo e = 5 [default = PAPER]; - | enum Roshambo { - | ROCK = 0; - | SCISSORS = 1; - | PAPER = 2; - | } - |} - """.trimMargin() - ) - .schema() - - val message = schema.getType("Message") as MessageType - assertThat(message.field("a")!!.default).isNull() - assertThat(message.field("b")!!.default).isEqualTo("5") - assertThat(message.field("c")!!.default).isEqualTo("true") - assertThat(message.field("d")!!.default).isEqualTo("foo") - assertThat(message.field("e")!!.default).isEqualTo("PAPER") - } - - @Test - fun fieldOptions() { - val schema = RepoBuilder() - .add("message.proto", """ - |import "google/protobuf/descriptor.proto"; - |message Message { - | optional int32 a = 1; - | optional int32 b = 2 [color=red, deprecated=true, packed=true]; - |} - |extend google.protobuf.FieldOptions { - | optional string color = 60001; - |} - """.trimMargin() - ) - .schema() - val message = schema.getType("Message") as MessageType - - val aOptions = message.field("a")!!.options() - assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "color"))).isNull() - assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "deprecated"))).isNull() - assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "packed"))).isNull() - - val bOptions = message.field("b")!!.options() - assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "color"))).isEqualTo("red") - assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "deprecated"))).isEqualTo(true) - assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "packed"))).isEqualTo(true) - } - - @Test - fun duplicateOption() { - try { - RepoBuilder() - .add("message.proto", """ - |import "google/protobuf/descriptor.proto"; - |message Message { - | optional int32 a = 1 [color=red, color=blue]; - |} - |extend google.protobuf.FieldOptions { - | optional string color = 60001; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |conflicting options: red, blue - | for field a (/source/message.proto at 3:3) - | in message Message (/source/message.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun messageFieldTypeUnknown() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | optional foo_package.Foo unknown = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for field unknown (/source/message.proto at 2:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun oneofFieldTypeUnknown() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | oneof selection { - | int32 known = 1; - | foo_package.Foo unknown = 2; - | } - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for field unknown (/source/message.proto at 4:5) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun serviceTypesMustBeNamed() { - try { - RepoBuilder() - .add("service.proto", """ - |service Service { - | rpc Call (string) returns (Response); - |} - |message Response { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |expected a message but was string - | for rpc Call (/source/service.proto at 2:3) - | in service Service (/source/service.proto at 1:1) - """.trimMargin() - ) - } - - try { - RepoBuilder() - .add("service.proto", """ - |service Service { - | rpc Call (Request) returns (string); - |} - |message Request { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |expected a message but was string - | for rpc Call (/source/service.proto at 2:3) - | in service Service (/source/service.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun serviceTypesUnknown() { - try { - RepoBuilder() - .add("service.proto", """ - |service Service { - | rpc Call (foo_package.Foo) returns (Response); - |} - |message Response { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for rpc Call (/source/service.proto at 2:3) - | in service Service (/source/service.proto at 1:1) - """.trimMargin() - ) - } - - try { - RepoBuilder() - .add("service.proto", """ - |service Service { - | rpc Call (Request) returns (foo_package.Foo); - |} - |message Request { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for rpc Call (/source/service.proto at 2:3) - | in service Service (/source/service.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun extendedTypeUnknown() { - try { - RepoBuilder() - .add("extend.proto", """ - |extend foo_package.Foo { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for extend (/source/extend.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun extendedTypeMustBeNamed() { - try { - RepoBuilder() - .add("extend.proto", """ - |extend string { - | optional Value value = 1000; - |} - |message Value { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |expected a message but was string - | for extend (/source/extend.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun extendFieldTypeUnknown() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - |} - |extend Message { - | optional foo_package.Foo unknown = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for field unknown (/source/message.proto at 4:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun multipleErrors() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | optional foo_package.Foo unknown = 1; - | optional foo_package.Foo also_unknown = 2; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve foo_package.Foo - | for field unknown (/source/message.proto at 2:3) - | in message Message (/source/message.proto at 1:1) - |unable to resolve foo_package.Foo - | for field also_unknown (/source/message.proto at 3:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateMessageTagDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | required string name1 = 1; - | required string name2 = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple fields share tag 1: - | 1. name1 (/source/message.proto at 2:3) - | 2. name2 (/source/message.proto at 3:3) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateTagValueDisallowedInOneOf() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | required string name1 = 1; - | oneof selection { - | string name2 = 1; - | } - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple fields share tag 1: - | 1. name1 (/source/message.proto at 2:3) - | 2. name2 (/source/message.proto at 4:5) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateExtendTagDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - |} - |extend Message { - | optional string name1 = 1; - | optional string name2 = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple fields share tag 1: - | 1. name1 (/source/message.proto at 4:3) - | 2. name2 (/source/message.proto at 5:3) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun messageNameCollisionDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | optional string a = 1; - | optional string a = 2; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple fields share name a: - | 1. a (/source/message.proto at 2:3) - | 2. a (/source/message.proto at 3:3) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun messsageAndExtensionNameCollision() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - | optional string a = 1; - |} - """.trimMargin() - ) - .add("extend.proto", """ - |package p; - |import "message.proto"; - |extend Message { - | optional string a = 2; - |} - """.trimMargin() - ) - .schema() - val messageType = schema.getType("Message") as MessageType - - assertThat(messageType.field("a")!!.tag()).isEqualTo(1) - assertThat(messageType.extensionField("p.a")!!.tag()).isEqualTo(2) - } - - @Test - fun extendNameCollisionInSamePackageDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - |} - """.trimMargin()) - .add("extend1.proto", """ - |import "message.proto"; - |extend Message { - | optional string a = 1; - |} - """.trimMargin()) - .add("extend2.proto", """ - |import "message.proto"; - |extend Message { - | optional string a = 2; - |} - """.trimMargin()) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple fields share name a: - | 1. a (/source/extend1.proto at 3:3) - | 2. a (/source/extend2.proto at 3:3) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun extendNameCollisionInDifferentPackagesAllowed() { - val schema = RepoBuilder() - .add("message.proto", """ - |message Message { - |} - """.trimMargin() - ) - .add("extend1.proto", """ - |package p1; - |import "message.proto"; - |extend Message { - | optional string a = 1; - |} - """.trimMargin() - ) - .add("extend2.proto", """ - |package p2; - |import "message.proto"; - |extend Message { - | optional string a = 2; - |} - """.trimMargin() - ) - .schema() - val messageType = schema.getType("Message") as MessageType - - assertThat(messageType.field("a")).isNull() - assertThat(messageType.extensionField("p1.a")!!.packageName()).isEqualTo("p1") - assertThat(messageType.extensionField("p2.a")!!.packageName()).isEqualTo("p2") - } - - @Test - fun extendEnumDisallowed() { - try { - RepoBuilder() - .add("enum.proto", """ - |enum Enum { - | A = 1; - | B = 2; - |} - """.trimMargin() - ) - .add("extend.proto", """ - |import "enum.proto"; - |extend Enum { - | optional string a = 2; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |expected a message but was Enum - | for extend (/source/extend.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun requiredExtendFieldDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - |} - |extend Message { - | required string a = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |extension fields cannot be required - | for field a (/source/message.proto at 4:3) - | in message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun oneofLabelDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | oneof string s = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage("Syntax error in /source/message.proto at 2:17: expected '{'") - } - } - - @Test - fun duplicateEnumValueTagInScopeDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |message Message { - | enum Enum1 { - | VALUE = 1; - | } - | enum Enum2 { - | VALUE = 2; - | } - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple enums share constant VALUE: - | 1. Message.Enum1.VALUE (/source/message.proto at 3:5) - | 2. Message.Enum2.VALUE (/source/message.proto at 6:5) - | for message Message (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateEnumConstantTagWithoutAllowAliasDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |enum Enum { - | A = 1; - | B = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple enum constants share tag 1: - | 1. A (/source/message.proto at 2:3) - | 2. B (/source/message.proto at 3:3) - | for enum Enum (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateEnumConstantTagWithAllowAliasFalseDisallowed() { - try { - RepoBuilder() - .add("message.proto", """ - |enum Enum { - | option allow_alias = false; - | A = 1; - | B = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |multiple enum constants share tag 1: - | 1. A (/source/message.proto at 3:3) - | 2. B (/source/message.proto at 4:3) - | for enum Enum (/source/message.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun duplicateEnumConstantTagWithAllowAliasTrueAllowed() { - val schema = RepoBuilder() - .add("message.proto", """ - |enum Enum { - | option allow_alias = true; - | A = 1; - | B = 1; - |} - """.trimMargin() - ) - .schema() - val enumType = schema.getType("Enum") as EnumType - assertThat(enumType.constant("A")!!.tag).isEqualTo(1) - assertThat(enumType.constant("B")!!.tag).isEqualTo(1) - } - - @Test - fun fieldTypeImported() { - val schema = RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |message A { - | optional pb.B b = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - val a = schema.getType("pa.A") as MessageType - val b = schema.getType("pb.B") as MessageType - assertThat(a.field("b")!!.type()).isEqualTo(b.type()) - } - - @Test - fun fieldMapTypeImported() { - val schema = RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |message A { - | map b = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - val a = schema.getType("pa.A") as MessageType - val b = schema.getType("pb.B") as MessageType - assertThat(a.field("b")!!.type().valueType()).isEqualTo(b.type()) - } - - @Test - fun fieldTypeNotImported() { - try { - RepoBuilder() - .add("a.proto", """ - |package pa; - |message A { - | optional pb.B b = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |a.proto needs to import b.proto - | for field b (/source/a.proto at 3:3) - | in message pa.A (/source/a.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun fieldMapTypeNotImported() { - try { - RepoBuilder() - .add("a.proto", """ - |package pa; - |message A { - | map b = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |a.proto needs to import b.proto - | for field b (/source/a.proto at 3:3) - | in message pa.A (/source/a.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun rpcTypeImported() { - val schema = RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |service Service { - | rpc Call (pb.B) returns (pb.B); - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - val service = schema.getService("pa.Service") - val b = schema.getType("pb.B") as MessageType - assertThat(service.rpcs()[0].requestType()).isEqualTo(b.type()) - assertThat(service.rpcs()[0].responseType()).isEqualTo(b.type()) - } - - @Test - fun rpcTypeNotImported() { - try { - RepoBuilder() - .add("a.proto", """ - |package pa; - |service Service { - | rpc Call (pb.B) returns (pb.B); - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |a.proto needs to import b.proto - | for rpc Call (/source/a.proto at 3:3) - | in service pa.Service (/source/a.proto at 2:1) - |a.proto needs to import b.proto - | for rpc Call (/source/a.proto at 3:3) - | in service pa.Service (/source/a.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun extendTypeImported() { - val schema = RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |extend pb.B { - | optional string a = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - | extensions 1; - |} - """.trimMargin() - ) - .schema() - val extendB = schema.protoFiles()[0].extendList()[0] - val b = schema.getType("pb.B") as MessageType - assertThat(extendB.type()).isEqualTo(b.type()) - } - - @Test - fun extendTypeNotImported() { - try { - RepoBuilder() - .add("a.proto", """ - |package pa; - |extend pb.B { - | optional string a = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |message B { - | extensions 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |a.proto needs to import b.proto - | for extend pb.B (/source/a.proto at 2:1) - """.trimMargin() - ) - } - } - - @Test - fun transitiveImportNotFollowed() { - try { - RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |message A { - | optional pc.C c = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |import "c.proto"; - |message B { - |} - """.trimMargin() - ) - .add("c.proto", """ - |package pc; - |message C { - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected.message).isEqualTo(""" - |a.proto needs to import c.proto - | for field c (/source/a.proto at 4:3) - | in message pa.A (/source/a.proto at 3:1) - """.trimMargin() - ) - } - } - - @Test - fun transitivePublicImportFollowed() { - val schema = RepoBuilder() - .add("a.proto", """ - |package pa; - |import "b.proto"; - |message A { - | optional pc.C c = 1; - |} - """.trimMargin() - ) - .add("b.proto", """ - |package pb; - |import public "c.proto"; - |message B { - |} - """.trimMargin() - ) - .add("c.proto", """ - |package pc; - |message C { - |} - """.trimMargin() - ) - .schema() - val a = schema.getType("pa.A") as MessageType - val c = schema.getType("pc.C") as MessageType - assertThat(a.field("c")!!.type()).isEqualTo(c.type()) - } - - @Test - fun importSamePackageDifferentFile() { - val schema = RepoBuilder() - .add("a_b_1.proto", """ - |package a.b; - | - |import "a_b_2.proto"; - | - |message MessageB { - | optional .a.b.MessageC c1 = 1; - | optional a.b.MessageC c2 = 2; - | optional b.MessageC c3 = 3; - | optional MessageC c4 = 4; - |} - """.trimMargin() - ) - .add("a_b_2.proto", """ - |package a.b; - | - |message MessageC { - |} - """.trimMargin() - ) - .schema() - val messageC = schema.getType("a.b.MessageB") as MessageType - assertThat(messageC.field("c1")!!.type()).isEqualTo(ProtoType.get("a.b.MessageC")) - assertThat(messageC.field("c2")!!.type()).isEqualTo(ProtoType.get("a.b.MessageC")) - assertThat(messageC.field("c3")!!.type()).isEqualTo(ProtoType.get("a.b.MessageC")) - assertThat(messageC.field("c4")!!.type()).isEqualTo(ProtoType.get("a.b.MessageC")) - } - - @Test - fun importResolvesEnclosingPackageSuffix() { - val schema = RepoBuilder() - .add("a_b.proto", """ - |package a.b; - | - |message MessageB { - |} - """.trimMargin() - ) - .add("a_b_c.proto", """ - |package a.b.c; - | - |import "a_b.proto"; - | - |message MessageC { - | optional b.MessageB message_b = 1; - |} - """.trimMargin() - ) - .schema() - val messageC = schema.getType("a.b.c.MessageC") as MessageType - assertThat(messageC.field("message_b")!!.type()).isEqualTo(ProtoType.get("a.b.MessageB")) - } - - @Test - fun importResolvesNestedPackageSuffix() { - val schema = RepoBuilder() - .add("a_b.proto", """ - |package a.b; - | - |import "a_b_c.proto"; - | - |message MessageB { - | optional c.MessageC message_c = 1; - |} - """.trimMargin() - ) - .add("a_b_c.proto", """ - |package a.b.c; - | - |message MessageC { - |} - """.trimMargin() - ) - .schema() - val messageC = schema.getType("a.b.MessageB") as MessageType - assertThat(messageC.field("message_c")!!.type()).isEqualTo(ProtoType.get("a.b.c.MessageC")) - } - - @Test - fun nestedPackagePreferredOverEnclosingPackage() { - val schema = RepoBuilder() - .add("a.proto", """ - |package a; - | - |message MessageA { - |} - """.trimMargin() - ) - .add("a_b.proto", """ - |package a.b; - | - |import "a.proto"; - |import "a_b_a.proto"; - | - |message MessageB { - | optional a.MessageA message_a = 1; - |} - """.trimMargin() - ) - .add("a_b_a.proto", """ - |package a.b.a; - | - |message MessageA { - |} - """.trimMargin() - ) - .schema() - val messageC = schema.getType("a.b.MessageB") as MessageType - assertThat(messageC.field("message_a")!!.type()).isEqualTo(ProtoType.get("a.b.a.MessageA")) - } - - @Test - fun dotPrefixRefersToRootPackage() { - val schema = RepoBuilder() - .add("a.proto", """ - |package a; - | - |message MessageA { - |} - """.trimMargin() - ) - .add("a_b.proto", """ - |package a.b; - | - |import "a.proto"; - |import "a_b_a.proto"; - | - |message MessageB { - | optional .a.MessageA message_a = 1; - |} - """.trimMargin() - ) - .add("a_b_a.proto", """ - |package a.b.a; - | - |message MessageA { - |} - """.trimMargin() - ) - .schema() - val messageC = schema.getType("a.b.MessageB") as MessageType - assertThat(messageC.field("message_a")!!.type()).isEqualTo(ProtoType.get("a.MessageA")) - } - - @Test - fun dotPrefixMustBeRoot() { - try { - RepoBuilder() - .add("a_b.proto", """ - |package a.b; - | - |message MessageB { - |} - """.trimMargin() - ) - .add("a_b_c.proto", """ - |package a.b.c; - | - |import "a_b.proto"; - | - |message MessageC { - | optional .b.MessageB message_b = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |unable to resolve .b.MessageB - | for field message_b (/source/a_b_c.proto at 6:3) - | in message a.b.c.MessageC (/source/a_b_c.proto at 5:1) - """.trimMargin() - ) - } - } - - @Test - fun groupsThrow() { - try { - RepoBuilder() - .add("test.proto", """ - |message SearchResponse { - | repeated group Result = 1 { - | required string url = 2; - | optional string title = 3; - | repeated string snippets = 4; - | } - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage("/source/test.proto at 2:3: 'group' is not supported") - } - } - - @Test - fun oneOfGroupsThrow() { - try { - RepoBuilder() - .add("test.proto", """ - |message Message { - | oneof hi { - | string name = 1; - | - | group Stuff = 3 { - | optional int32 result_per_page = 4; - | optional int32 page_count = 5; - | } - | } - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage("/source/test.proto at 5:5: 'group' is not supported") - } - } - - @Test - fun reservedTagThrowsWhenUsed() { - try { - RepoBuilder() - .add("test.proto", """ - |message Message { - | reserved 1; - | optional string name = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |tag 1 is reserved (/source/test.proto at 2:3) - | for field name (/source/test.proto at 3:3) - | in message Message (/source/test.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun reservedTagRangeThrowsWhenUsed() { - try { - RepoBuilder() - .add("test.proto", """ - |message Message { - | reserved 1 to 3; - | optional string name = 2; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |tag 2 is reserved (/source/test.proto at 2:3) - | for field name (/source/test.proto at 3:3) - | in message Message (/source/test.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun reservedNameThrowsWhenUsed() { - try { - RepoBuilder() - .add("test.proto", """ - |message Message { - | reserved 'foo'; - | optional string foo = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |name 'foo' is reserved (/source/test.proto at 2:3) - | for field foo (/source/test.proto at 3:3) - | in message Message (/source/test.proto at 1:1) - """.trimMargin() - ) - } - } - - @Test - fun reservedTagAndNameBothReported() { - try { - RepoBuilder() - .add("test.proto", """ - |message Message { - | reserved 'foo'; - | reserved 1; - | optional string foo = 1; - |} - """.trimMargin() - ) - .schema() - Assertions.assertTrue(false) - } catch (expected: SchemaException) { - assertThat(expected).hasMessage(""" - |name 'foo' is reserved (/source/test.proto at 2:3) - | for field foo (/source/test.proto at 4:3) - | in message Message (/source/test.proto at 1:1) - |tag 1 is reserved (/source/test.proto at 3:3) - | for field foo (/source/test.proto at 4:3) - | in message Message (/source/test.proto at 1:1) - """.trimMargin() - ) - } - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/UtilTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/UtilTest.kt deleted file mode 100644 index 861b42a7..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/UtilTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal - -import com.squareup.wire.schema.internal.Util.appendDocumentation -import com.squareup.wire.schema.internal.Util.appendIndented -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class UtilTest { - @Test - fun indentationTest() { - val input = """ - |Foo - |Bar - |Baz""".trimMargin() - val expected = """ - | Foo - | Bar - | Baz - |""".trimMargin() - val actual = buildString { - appendIndented(this, input) - } - assertThat(actual).isEqualTo(expected) - } - - @Test - fun documentationTest() { - val input = "Foo\nBar\nBaz" - val expected = """ - |// Foo - |// Bar - |// Baz - |""".trimMargin() - val actual = buildString { - appendDocumentation(this, input) - } - assertThat(actual).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/EnumElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/EnumElementTest.kt deleted file mode 100644 index 10d07aac..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/EnumElementTest.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.STRING -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class EnumElementTest { - internal var location = Location.get("file.proto") - - @Test - fun emptyToSchema() { - val element = EnumElement( - location = location, - name = "Enum" - ) - val expected = "enum Enum {}\n" - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleToSchema() { - val element = EnumElement( - location = location, - name = "Enum", - constants = listOf( - EnumConstantElement(location = location, name = "ONE", tag = 1), - EnumConstantElement(location = location, name = "TWO", tag = 2), - EnumConstantElement(location = location, name = "SIX", tag = 6) - ) - ) - val expected = """ - |enum Enum { - | ONE = 1; - | TWO = 2; - | SIX = 6; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleConstants() { - val one = EnumConstantElement(location = location, name = "ONE", tag = 1) - val two = EnumConstantElement(location = location, name = "TWO", tag = 2) - val six = EnumConstantElement(location = location, name = "SIX", tag = 6) - val element = EnumElement( - location = location, - name = "Enum", - constants = listOf(one, two, six) - ) - assertThat(element.constants).hasSize(3) - } - - @Test - fun simpleWithOptionsToSchema() { - val element = EnumElement( - location = location, - name = "Enum", - options = listOf(OptionElement.create("kit", STRING, "kat")), - constants = listOf( - EnumConstantElement(location = location, name = "ONE", tag = 1), - EnumConstantElement(location = location, name = "TWO", tag = 2), - EnumConstantElement(location = location, name = "SIX", tag = 6) - ) - ) - val expected = """ - |enum Enum { - | option kit = "kat"; - | - | ONE = 1; - | TWO = 2; - | SIX = 6; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleOptions() { - val kitKat = OptionElement.create("kit", STRING, "kat") - val fooBar = OptionElement.create("foo", STRING, "bar") - val element = EnumElement( - location = location, - name = "Enum", - options = listOf(kitKat, fooBar), - constants = listOf(EnumConstantElement(location = location, name = "ONE", tag = 1)) - ) - assertThat(element.options).hasSize(2) - } - - @Test - fun simpleWithDocumentationToSchema() { - val element = EnumElement( - location = location, - name = "Enum", - documentation = "Hello", - constants = - listOf( - EnumConstantElement(location = location, name = "ONE", tag = 1), - EnumConstantElement(location = location, name = "TWO", tag = 2), - EnumConstantElement(location = location, name = "SIX", tag = 6) - ) - ) - val expected = """ - |// Hello - |enum Enum { - | ONE = 1; - | TWO = 2; - | SIX = 6; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldToSchema() { - val value = EnumConstantElement(location = location, name = "NAME", tag = 1) - val expected = "NAME = 1;\n" - assertThat(value.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldWithDocumentationToSchema() { - val value = EnumConstantElement( - location = location, - name = "NAME", - tag = 1, - documentation = "Hello" - ) - val expected = """ - |// Hello - |NAME = 1; - |""".trimMargin() - assertThat(value.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldWithOptionsToSchema() { - val value = EnumConstantElement( - location = location, - name = "NAME", - tag = 1, - options = listOf( - OptionElement.create("kit", STRING, "kat", true), - OptionElement.create("tit", STRING, "tat") - ) - ) - val expected = """ - |NAME = 1 [ - | (kit) = "kat", - | tit = "tat" - |]; - |""".trimMargin() - assertThat(value.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtendElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtendElementTest.kt deleted file mode 100644 index af443459..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtendElementTest.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Field.Label.REQUIRED -import com.squareup.wire.schema.Location -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class ExtendElementTest { - internal var location = Location.get("file.proto") - - @Test - fun emptyToSchema() { - val extend = ExtendElement( - location = location, - name = "Name" - ) - val expected = "extend Name {}\n" - assertThat(extend.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleToSchema() { - val extend = ExtendElement( - location = location, - name = "Name", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - val expected = """ - |extend Name { - | required string name = 1; - |} - |""".trimMargin() - assertThat(extend.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleFields() { - val firstName = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "first_name", - tag = 1 - ) - val lastName = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "last_name", - tag = 2 - ) - val extend = ExtendElement( - location = location, - name = "Name", - fields = listOf(firstName, lastName) - ) - assertThat(extend.fields).hasSize(2) - } - - @Test - fun simpleWithDocumentationToSchema() { - val extend = ExtendElement( - location = location, - name = "Name", - documentation = "Hello", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - val expected = """ - |// Hello - |extend Name { - | required string name = 1; - |} - |""".trimMargin() - assertThat(extend.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtensionsElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtensionsElementTest.kt deleted file mode 100644 index f69399f1..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ExtensionsElementTest.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.Util -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class ExtensionsElementTest { - internal var location = Location.get("file.proto") - - @Test - fun singleValueToSchema() { - val actual = ExtensionsElement( - location = location, - start = 500, - end = 500 - ) - val expected = "extensions 500;\n" - assertThat(actual.toSchema()).isEqualTo(expected) - } - - @Test - fun rangeToSchema() { - val actual = ExtensionsElement( - location = location, - start = 500, - end = 505 - ) - val expected = "extensions 500 to 505;\n" - assertThat(actual.toSchema()).isEqualTo(expected) - } - - @Test - fun maxRangeToSchema() { - val actual = ExtensionsElement( - location = location, - start = 500, - end = Util.MAX_TAG_VALUE - ) - val expected = "extensions 500 to max;\n" - assertThat(actual.toSchema()).isEqualTo(expected) - } - - @Test - fun withDocumentationToSchema() { - val actual = ExtensionsElement( - location = location, - documentation = "Hello", - start = 500, - end = 500 - ) - val expected = """ - |// Hello - |extensions 500; - |""".trimMargin() - assertThat(actual.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/FieldElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/FieldElementTest.kt deleted file mode 100644 index 8520ec41..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/FieldElementTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2015 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Field.Label.OPTIONAL -import com.squareup.wire.schema.Field.Label.REQUIRED -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class FieldElementTest { - internal var location = Location.get("file.proto") - - @Test - fun field() { - val field = FieldElement( - location = location, - label = OPTIONAL, - type = "CType", - name = "ctype", - tag = 1, - options = listOf( - OptionElement.create("default", Kind.ENUM, "TEST"), - OptionElement.create("deprecated", Kind.BOOLEAN, true) - ) - ) - - assertThat(field.options) - .containsOnly( - OptionElement.create("default", Kind.ENUM, "TEST"), - OptionElement.create("deprecated", Kind.BOOLEAN, true) - ) - } - - @Test - fun addMultipleOptions() { - val kitKat = OptionElement.create("kit", Kind.STRING, "kat") - val fooBar = OptionElement.create("foo", Kind.STRING, "bar") - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1, - options = listOf(kitKat, fooBar) - ) - - assertThat(field.options).hasSize(2) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/MessageElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/MessageElementTest.kt deleted file mode 100644 index 8968355d..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/MessageElementTest.kt +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.google.common.collect.Range -import com.squareup.wire.schema.Field.Label.OPTIONAL -import com.squareup.wire.schema.Field.Label.REPEATED -import com.squareup.wire.schema.Field.Label.REQUIRED -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class MessageElementTest { - internal var location = Location.get("file.proto") - @Test - fun emptyToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val expected = "message Message {}\n" - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleToSchema() { - val element = MessageElement( - location = location, - name = "Message", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - val expected = """ - |message Message { - | required string name = 1; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleFields() { - val firstName = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "first_name", - tag = 1 - ) - val lastName = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "last_name", - tag = 2 - ) - val element = MessageElement( - location = location, - name = "Message", - fields = listOf(firstName, lastName) - ) - assertThat(element.fields).hasSize(2) - } - - @Test - fun simpleWithDocumentationToSchema() { - val element = MessageElement( - location = location, - name = "Message", - documentation = "Hello", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - val expected = """ - |// Hello - |message Message { - | required string name = 1; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleWithOptionsToSchema() { - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - val element = MessageElement( - location = location, - name = "Message", - fields = listOf(field), - options = listOf(OptionElement.create("kit", Kind.STRING, "kat")) - ) - val expected = - """message Message { - | option kit = "kat"; - | - | required string name = 1; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleOptions() { - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - val kitKat = OptionElement.create("kit", Kind.STRING, "kat") - val fooBar = OptionElement.create("foo", Kind.STRING, "bar") - val element = MessageElement( - location = location, - name = "Message", - fields = listOf(field), - options = listOf(kitKat, fooBar) - ) - assertThat(element.options).hasSize(2) - } - - @Test - fun simpleWithNestedElementsToSchema() { - val element = MessageElement( - location = location, - name = "Message", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ), - nestedTypes = listOf( - MessageElement( - location = location, - name = "Nested", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - ) - ) - val expected = """ - |message Message { - | required string name = 1; - | - | message Nested { - | required string name = 1; - | } - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleTypes() { - val nested1 = MessageElement( - location = location, - name = "Nested1") - val nested2 = MessageElement( - location = location, - name = "Nested2") - val element = MessageElement( - location = location, - name = "Message", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ), - nestedTypes = listOf(nested1, nested2) - ) - assertThat(element.nestedTypes).hasSize(2) - } - - @Test - fun simpleWithExtensionsToSchema() { - val element = MessageElement( - location = location, - name = "Message", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ), - extensions = listOf(ExtensionsElement(location = location, start = 500, end = 501)) - ) - val expected = """ - |message Message { - | required string name = 1; - | - | extensions 500 to 501; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleExtensions() { - val fives = ExtensionsElement(location = location, start = 500, end = 501) - val sixes = ExtensionsElement(location = location, start = 600, end = 601) - val element = MessageElement( - location = location, - name = "Message", - fields = listOf( - FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - ), - extensions = listOf(fives, sixes) - ) - assertThat(element.extensions).hasSize(2) - } - - @Test - fun oneOfToSchema() { - val element = MessageElement( - location = location, - name = "Message", - oneOfs = listOf( - OneOfElement( - name = "hi", - fields = listOf( - FieldElement( - location = location, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - ) - ) - val expected = """ - |message Message { - | oneof hi { - | string name = 1; - | } - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun oneOfWithGroupToSchema() { - val element = MessageElement( - location = location, - name = "Message", - oneOfs = listOf( - OneOfElement( - name = "hi", - fields = listOf( - FieldElement( - location = location, - type = "string", - name = "name", - tag = 1 - ) - ), - groups = listOf( - GroupElement( - location = location.at(5, 5), - name = "Stuff", - tag = 3, - fields = listOf( - FieldElement( - location = location.at(6, 7), - label = OPTIONAL, - type = "int32", - name = "result_per_page", - tag = 4 - ), - FieldElement( - location = location.at(7, 7), - label = OPTIONAL, - type = "int32", - name = "page_count", - tag = 5 - ) - ) - ) - ) - ) - ) - ) - val expected = """ - |message Message { - | oneof hi { - | string name = 1; - | - | group Stuff = 3 { - | optional int32 result_per_page = 4; - | optional int32 page_count = 5; - | } - | } - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleOneOfs() { - val hi = OneOfElement( - name = "hi", - fields = listOf( - FieldElement( - location = location, - type = "string", - name = "name", - tag = 1 - ) - ) - ) - val hey = OneOfElement( - name = "hey", - fields = listOf( - FieldElement( - location = location, - type = "string", - name = "city", - tag = 2 - ) - ) - ) - val element = MessageElement( - location = location, - name = "Message", - oneOfs = listOf(hi, hey) - ) - assertThat(element.oneOfs).hasSize(2) - } - - @Test - fun reservedToSchema() { - val element = MessageElement( - location = location, - name = "Message", - reserveds = listOf( - ReservedElement(location = location, values = listOf(10, Range.closed(12, 14), "foo")), - ReservedElement(location = location, values = listOf(10)), - ReservedElement(location = location, values = listOf(Range.closed(12, 14))), - ReservedElement(location = location, values = listOf("foo")) - ) - ) - val expected = """ - |message Message { - | reserved 10, 12 to 14, "foo"; - | reserved 10; - | reserved 12 to 14; - | reserved "foo"; - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun groupToSchema() { - val element = MessageElement( - location = location.at(1, 1), - name = "SearchResponse", - groups = listOf( - GroupElement( - location = location.at(2, 3), - label = REPEATED, - name = "Result", - tag = 1, - fields = listOf( - FieldElement( - location = location.at(3, 5), - label = REQUIRED, - type = "string", - name = "url", - tag = 2 - ), - FieldElement( - location = location.at(4, 5), - label = OPTIONAL, - type = "string", - name = "title", - tag = 3 - ), - FieldElement( - location = location.at(5, 5), - label = REPEATED, - type = "string", - name = "snippets", - tag = 4 - ) - ) - ) - ) - ) - val expected = """ - |message SearchResponse { - | repeated group Result = 1 { - | required string url = 2; - | optional string title = 3; - | repeated string snippets = 4; - | } - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun multipleEverythingToSchema() { - val field1 = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - val field2 = FieldElement( - location = location, - label = REQUIRED, - type = "bool", - name = "other_name", - tag = 2 - ) - val oneOf1Field = FieldElement( - location = location, - type = "string", - name = "namey", - tag = 3 - ) - val oneOf1 = OneOfElement( - name = "thingy", - fields = listOf(oneOf1Field) - ) - val oneOf2Field = FieldElement( - location = location, - type = "string", - name = "namer", - tag = 4 - ) - val oneOf2 = OneOfElement( - name = "thinger", - fields = listOf(oneOf2Field) - ) - val extensions1 = ExtensionsElement(location = location, start = 500, end = 501) - val extensions2 = ExtensionsElement(location = location, start = 503, end = 503) - val nested = MessageElement( - location = location, - name = "Nested", - fields = listOf(field1) - ) - val option = OptionElement.create("kit", Kind.STRING, "kat") - val element = MessageElement( - location = location, - name = "Message", - fields = listOf(field1, field2), - oneOfs = listOf(oneOf1, oneOf2), - nestedTypes = listOf(nested), - extensions = listOf(extensions1, extensions2), - options = listOf(option) - ) - val expected = """ - |message Message { - | option kit = "kat"; - | - | required string name = 1; - | required bool other_name = 2; - | - | oneof thingy { - | string namey = 3; - | } - | oneof thinger { - | string namer = 4; - | } - | - | extensions 500 to 501; - | extensions 503; - | - | message Nested { - | required string name = 1; - | } - |} - |""".trimMargin() - assertThat(element.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldToSchema() { - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1 - ) - val expected = "required string name = 1;\n" - assertThat(field.toSchema()).isEqualTo(expected) - } - - @Test - fun oneOfFieldToSchema() { - val field = FieldElement( - location = location, - type = "string", - name = "name", - tag = 1 - ) - val expected = "string name = 1;\n" - assertThat(field.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldWithDocumentationToSchema() { - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1, - documentation = "Hello" - ) - val expected = - """// Hello - |required string name = 1; - |""".trimMargin() - assertThat(field.toSchema()).isEqualTo(expected) - } - - @Test - fun fieldWithOptionsToSchema() { - val field = FieldElement( - location = location, - label = REQUIRED, - type = "string", - name = "name", - tag = 1, - options = listOf(OptionElement.create("kit", Kind.STRING, "kat")) - ) - val expected = - """required string name = 1 [ - | kit = "kat" - |]; - |""".trimMargin() - assertThat(field.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/OptionElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/OptionElementTest.kt deleted file mode 100644 index 0dc9363e..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/OptionElementTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.BOOLEAN -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.LIST -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.MAP -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.OPTION -import com.squareup.wire.schema.internal.parser.OptionElement.Kind.STRING -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class OptionElementTest { - @Test - fun simpleToSchema() { - val option = OptionElement.create("foo", STRING, "bar") - val expected = """foo = "bar"""" - assertThat(option.toSchema()).isEqualTo(expected) - } - - @Test - fun nestedToSchema() { - val option = - OptionElement.create("foo.boo", OPTION, OptionElement.create("bar", STRING, "baz"), true) - val expected = """(foo.boo).bar = "baz"""" - assertThat(option.toSchema()).isEqualTo(expected) - } - - @Test - fun listToSchema() { - val option = OptionElement.create( - "foo", - LIST, - listOf( - OptionElement.create("ping", STRING, "pong", true), - OptionElement.create("kit", STRING, "kat") - ), - true - ) - val expected = """ - |(foo) = [ - | (ping) = "pong", - | kit = "kat" - |] - """.trimMargin() - assertThat(option.toSchema()).isEqualTo(expected) - } - - @Test - fun mapToSchema() { - val option = OptionElement.create( - "foo", MAP, mapOf("ping" to "pong", "kit" to listOf("kat", "kot")) - ) - val expected = """ - |foo = { - | ping: "pong", - | kit: [ - | "kat", - | "kot" - | ] - |} - """.trimMargin() - assertThat(option.toSchema()).isEqualTo(expected) - } - - @Test - fun booleanToSchema() { - val option = OptionElement.create("foo", BOOLEAN, "false") - val expected = "foo = false" - assertThat(option.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ParsingTester.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ParsingTester.kt deleted file mode 100644 index effd29af..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ParsingTester.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import okio.buffer -import okio.source -import java.io.File -import java.util.ArrayDeque -import java.util.Collections - -/** Recursively traverse a directory and attempt to parse all of its proto files. */ -object ParsingTester { - /** Directory under which to search for protos. Change as needed. */ - private val ROOT = File("/path/to/protos") - - @JvmStatic - fun main() { - var total = 0 - var failed = 0 - - val fileQueue = ArrayDeque() - fileQueue.add(ROOT) - while (fileQueue.isNotEmpty()) { - val file = fileQueue.removeFirst() - if (file.isDirectory) { - Collections.addAll(fileQueue, *file.listFiles()!!) - } else if (file.name.endsWith(".proto")) { - println("Parsing ${file.path}") - total += 1 - - try { - file.source() - .buffer() - .use { `in` -> - val data = `in`.readUtf8() - ProtoParser.parse(Location.get(file.path), data) - } - } catch (e: Exception) { - e.printStackTrace() - failed += 1 - } - } - } - - println("\nTotal: $total Failed: $failed") - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoFileElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoFileElementTest.kt deleted file mode 100644 index 05fe97b6..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoFileElementTest.kt +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.ProtoFile.Syntax.PROTO_2 -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class ProtoFileElementTest { - internal var location = Location.get("file.proto") - - @Test - fun emptyToSchema() { - val file = ProtoFileElement(location = location) - val expected = "// file.proto\n" - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun emptyWithPackageToSchema() { - val file = ProtoFileElement( - location = location, - packageName = "example.simple" - ) - val expected = """ - |// file.proto - |package example.simple; - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement( - location = location, - types = listOf(element) - ) - val expected = """ - |// file.proto - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleWithImportsToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement( - location = location, - imports = listOf("example.other"), - types = listOf(element) - ) - val expected = """ - |// file.proto - | - |import "example.other"; - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleDependencies() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement( - location = location, - imports = listOf("example.other", "example.another"), - types = listOf(element) - ) - assertThat(file.imports).hasSize(2) - } - - @Test - fun simpleWithPublicImportsToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement( - location = location, - publicImports = listOf("example.other"), - types = listOf(element) - ) - val expected = """ - |// file.proto - | - |import public "example.other"; - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultiplePublicDependencies() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement(location = location, - publicImports = listOf("example.other", "example.another"), - types = listOf(element) - ) - assertThat(file.publicImports).hasSize(2) - } - - @Test - fun simpleWithBothImportsToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val file = ProtoFileElement(location = location, - imports = listOf("example.thing"), - publicImports = listOf("example.other"), - types = listOf(element) - ) - val expected = """ - |// file.proto - | - |import "example.thing"; - |import public "example.other"; - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun simpleWithServicesToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val service = ServiceElement( - location = location, - name = "Service" - ) - val file = ProtoFileElement( - location = location, - types = listOf(element), - services = listOf(service) - ) - val expected = """ - |// file.proto - | - |message Message {} - | - |service Service {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleServices() { - val service1 = ServiceElement( - location = location, - name = "Service1" - ) - val service2 = ServiceElement( - location = location, - name = "Service2" - ) - val file = ProtoFileElement( - location = location, - services = listOf(service1, service2) - ) - assertThat(file.services).hasSize(2) - } - - @Test - fun simpleWithOptionsToSchema() { - val element = MessageElement( - location = location, - name = "Message" - ) - val option = OptionElement.create("kit", Kind.STRING, "kat") - val file = ProtoFileElement( - location = location, - options = listOf(option), - types = listOf(element) - ) - val expected = """ - |// file.proto - | - |option kit = "kat"; - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleOptions() { - val element = MessageElement( - location = location, - name = "Message" - ) - val kitKat = OptionElement.create("kit", Kind.STRING, "kat") - val fooBar = OptionElement.create("foo", Kind.STRING, "bar") - val file = ProtoFileElement( - location = location, - options = listOf(kitKat, fooBar), - types = listOf(element) - ) - assertThat(file.options).hasSize(2) - } - - @Test - fun simpleWithExtendsToSchema() { - val file = ProtoFileElement( - location = location, - extendDeclarations = listOf(ExtendElement(location = location.at(5, 1), name = "Extend")), - types = listOf(MessageElement(location = location, name = "Message")) - ) - val expected = """ - |// file.proto - | - |message Message {} - | - |extend Extend {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleExtends() { - val extend1 = ExtendElement(location = location, name = "Extend1") - val extend2 = ExtendElement(location = location, name = "Extend2") - val file = ProtoFileElement( - location = location, - extendDeclarations = listOf(extend1, extend2) - ) - assertThat(file.extendDeclarations).hasSize(2) - } - - @Test - fun multipleEverythingToSchema() { - val element1 = MessageElement(location = location.at(10, 1), name = "Message1") - val element2 = MessageElement(location = location.at(11, 1), name = "Message2") - val extend1 = ExtendElement(location = location.at(13, 1), name = "Extend1") - val extend2 = ExtendElement(location = location.at(14, 1), name = "Extend2") - val option1 = OptionElement.create("kit", Kind.STRING, "kat") - val option2 = OptionElement.create("foo", Kind.STRING, "bar") - val service1 = ServiceElement( - location = location.at(16, 1), - name = "Service1" - ) - val service2 = ServiceElement( - location = location.at(17, 1), - name = "Service2" - ) - val file = ProtoFileElement( - location = location, - packageName = "example.simple", - imports = listOf("example.thing"), - publicImports = listOf("example.other"), - types = listOf(element1, element2), - services = listOf(service1, service2), - extendDeclarations = listOf(extend1, extend2), - options = listOf(option1, option2) - ) - val expected = """ - |// file.proto - |package example.simple; - | - |import "example.thing"; - |import public "example.other"; - | - |option kit = "kat"; - |option foo = "bar"; - | - |message Message1 {} - |message Message2 {} - | - |extend Extend1 {} - |extend Extend2 {} - | - |service Service1 {} - |service Service2 {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - - // Re-parse the expected string into a ProtoFile and ensure they're equal. - val parsed = ProtoParser.parse(location, expected) - assertThat(parsed).isEqualTo(file) - } - - @Test - fun syntaxToSchema() { - val element = MessageElement(location = location, name = "Message") - val file = ProtoFileElement( - location = location, - syntax = PROTO_2, - types = listOf(element) - ) - val expected = """ - |// file.proto - |syntax = "proto2"; - | - |message Message {} - |""".trimMargin() - assertThat(file.toSchema()).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoParserTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoParserTest.kt deleted file mode 100644 index d32c0edc..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ProtoParserTest.kt +++ /dev/null @@ -1,1840 +0,0 @@ -/* - * Copyright (C) 2013 Square, 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.squareup.wire.schema.internal.parser - -import com.google.common.collect.Range -import com.squareup.wire.schema.Field.Label.* -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.ProtoFile -import com.squareup.wire.schema.internal.Util -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import java.util.* - -class ProtoParserTest { - internal var location = Location.get("file.proto") - - @Test - fun typeParsing() { - val proto = """ - |message Types { - | required any f1 = 1; - | required bool f2 = 2; - | required bytes f3 = 3; - | required double f4 = 4; - | required float f5 = 5; - | required fixed32 f6 = 6; - | required fixed64 f7 = 7; - | required int32 f8 = 8; - | required int64 f9 = 9; - | required sfixed32 f10 = 10; - | required sfixed64 f11 = 11; - | required sint32 f12 = 12; - | required sint64 f13 = 13; - | required string f14 = 14; - | required uint32 f15 = 15; - | required uint64 f16 = 16; - | map f17 = 17; - | map f18 = 18; - | required arbitrary f19 = 19; - | required nested.nested f20 = 20; - |} - """.trimMargin() - - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "Types", fields = listOf( - FieldElement( - location = location.at(2, 3), label = REQUIRED, type = "any", name = "f1", tag = 1 - ), FieldElement( - location = location.at(3, 3), label = REQUIRED, type = "bool", name = "f2", tag = 2 - ), FieldElement( - location = location.at(4, 3), label = REQUIRED, type = "bytes", name = "f3", tag = 3 - ), FieldElement( - location = location.at(5, 3), label = REQUIRED, type = "double", name = "f4", tag = 4 - ), FieldElement( - location = location.at(6, 3), label = REQUIRED, type = "float", name = "f5", tag = 5 - ), FieldElement( - location = location.at(7, 3), label = REQUIRED, type = "fixed32", name = "f6", tag = 6 - ), FieldElement( - location = location.at(8, 3), label = REQUIRED, type = "fixed64", name = "f7", tag = 7 - ), FieldElement( - location = location.at(9, 3), label = REQUIRED, type = "int32", name = "f8", tag = 8 - ), FieldElement( - location = location.at(10, 3), label = REQUIRED, type = "int64", name = "f9", tag = 9 - ), FieldElement( - location = location.at(11, 3), label = REQUIRED, type = "sfixed32", name = "f10", tag = 10 - ), FieldElement( - location = location.at(12, 3), label = REQUIRED, type = "sfixed64", name = "f11", tag = 11 - ), FieldElement( - location = location.at(13, 3), label = REQUIRED, type = "sint32", name = "f12", tag = 12 - ), FieldElement( - location = location.at(14, 3), label = REQUIRED, type = "sint64", name = "f13", tag = 13 - ), FieldElement( - location = location.at(15, 3), label = REQUIRED, type = "string", name = "f14", tag = 14 - ), FieldElement( - location = location.at(16, 3), label = REQUIRED, type = "uint32", name = "f15", tag = 15 - ), FieldElement( - location = location.at(17, 3), label = REQUIRED, type = "uint64", name = "f16", tag = 16 - ), FieldElement( - location = location.at(18, 3), type = "map", name = "f17", tag = 17 - ), FieldElement( - location = location.at(19, 3), - type = "map", - name = "f18", - tag = 18 - ), FieldElement( - location = location.at(20, 3), label = REQUIRED, type = "arbitrary", name = "f19", tag = 19 - ), FieldElement( - location = location.at(21, 3), - label = REQUIRED, - type = "nested.nested", - name = "f20", - tag = 20 - ) - ) - ) - ) - ) - - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun mapWithLabelThrows() { - try { - ProtoParser.parse(location, "message Hey { required map a = 1; }") - Assertions.assertTrue(false) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage( - "Syntax error in file.proto at 1:15: 'map' type cannot have label" - ) - } - - try { - ProtoParser.parse(location, "message Hey { optional map a = 1; }") - Assertions.assertTrue(false) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage( - "Syntax error in file.proto at 1:15: 'map' type cannot have label" - ) - } - - try { - ProtoParser.parse(location, "message Hey { repeated map a = 1; }") - Assertions.assertTrue(false) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage( - "Syntax error in file.proto at 1:15: 'map' type cannot have label" - ) - } - } - - /** It looks like an option, but 'default' is special. It's missing from descriptor.proto! */ - @Test - fun defaultFieldOptionIsSpecial() { - val proto = """ - |message Message { - | required string a = 1 [default = "b", faulted = "c"]; - |} - |""".trimMargin() - - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "Message", fields = listOf( - FieldElement( - location = location.at(2, 3), - label = REQUIRED, - type = "string", - name = "a", - defaultValue = "b", - options = listOf(OptionElement.create("faulted", Kind.STRING, "c")), - tag = 1 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun singleLineComment() { - val proto = """ - |// Test all the things! - |message Test {} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo("Test all the things!") - } - - @Test - fun multipleSingleLineComments() { - val proto = """ - |// Test all - |// the things! - |message Test {} - """.trimMargin() - val expected = """ - |Test all - |the things! - """.trimMargin() - - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo(expected) - } - - @Test - fun singleLineJavadocComment() { - val proto = """ - |/** Test */ - |message Test {} - |""".trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo("Test") - } - - @Test - fun multilineJavadocComment() { - val proto = """ - |/** - | * Test - | * - | * Foo - | */ - |message Test {} - |""".trimMargin() - val expected = """ - |Test - | - |Foo - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo(expected) - } - - @Test - fun multipleSingleLineCommentsWithLeadingWhitespace() { - val proto = """ - |// Test - |// All - |// The - |// Things! - |message Test {} - """.trimMargin() - val expected = """ - |Test - | All - | The - | Things! - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo(expected) - } - - @Test - fun multilineJavadocCommentWithLeadingWhitespace() { - val proto = """ - |/** - | * Test - | * All - | * The - | * Things! - | */ - |message Test {} - """.trimMargin() - val expected = """ - |Test - | All - | The - | Things! - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo(expected) - } - - @Test - fun multilineJavadocCommentWithoutLeadingAsterisks() { - // We do not honor leading whitespace when the comment lacks leading asterisks. - val proto = """ - |/** - | Test - | All - | The - | Things! - | */ - |message Test {} - """.trimMargin() - val expected = """ - |Test - |All - |The - |Things! - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val type = parsed.types[0] - assertThat(type.documentation).isEqualTo(expected) - } - - @Test - fun messageFieldTrailingComment() { - // Trailing message field comment. - val proto = """ - |message Test { - | optional string name = 1; // Test all the things! - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val message = parsed.types[0] as MessageElement - val field = message.fields[0] - assertThat(field.documentation).isEqualTo("Test all the things!") - } - - @Test - fun messageFieldLeadingAndTrailingCommentAreCombined() { - val proto = """ - |message Test { - | // Test all... - | optional string name = 1; // ...the things! - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val message = parsed.types[0] as MessageElement - val field = message.fields[0] - assertThat(field.documentation).isEqualTo("Test all...\n...the things!") - } - - @Test - fun trailingCommentNotAssignedToFollowingField() { - val proto = """ - |message Test { - | optional string first_name = 1; // Testing! - | optional string last_name = 2; - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val message = parsed.types[0] as MessageElement - val field1 = message.fields[0] - assertThat(field1.documentation).isEqualTo("Testing!") - val field2 = message.fields[1] - assertThat(field2.documentation).isEqualTo("") - } - - @Test - fun enumValueTrailingComment() { - val proto = """ - |enum Test { - | FOO = 1; // Test all the things! - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val enumElement = parsed.types[0] as EnumElement - val value = enumElement.constants[0] - assertThat(value.documentation).isEqualTo("Test all the things!") - } - - @Test - fun trailingSinglelineComment() { - val proto = """ - |enum Test { - | FOO = 1; /* Test all the things! */ - | BAR = 2;/*Test all the things!*/ - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val enumElement = parsed.types[0] as EnumElement - val foo = enumElement.constants[0] - assertThat(foo.documentation).isEqualTo("Test all the things!") - val bar = enumElement.constants[1] - assertThat(bar.documentation).isEqualTo("Test all the things!") - } - - @Test - fun trailingMultilineComment() { - val proto = """ - |enum Test { - | FOO = 1; /* Test all the - |things! */ - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val enumElement = parsed.types[0] as EnumElement - val value = enumElement.constants[0] - assertThat(value.documentation).isEqualTo("Test all the\nthings!") - } - - @Test - fun trailingMultilineCommentMustBeLastOnLineThrows() { - val proto = """ - |enum Test { - | FOO = 1; /* Test all the things! */ BAR = 2; - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage( - "Syntax error in file.proto at 2:40: no syntax may follow trailing comment" - ) - } - } - - @Test - fun invalidTrailingComment() { - val proto = """ - |enum Test { - | FOO = 1; / - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage( - "Syntax error in file.proto at 2:12: expected '//' or '/*'" - ) - } - } - - @Test - fun enumValueLeadingAndTrailingCommentsAreCombined() { - val proto = """ - |enum Test { - | // Test all... - | FOO = 1; // ...the things! - |} - """.trimMargin() - val parsed = ProtoParser.parse(location, proto) - val enumElement = parsed.types[0] as EnumElement - val value = enumElement.constants[0] - assertThat(value.documentation).isEqualTo("Test all...\n...the things!") - } - - @Test - fun trailingCommentNotCombinedWhenEmpty() { - // Can't use raw strings here; otherwise, the formatter removes the trailing whitespace on line 3. - val proto = "enum Test {\n" + " // Test all...\n" + " FOO = 1; // \n" + "}" - val parsed = ProtoParser.parse(location, proto) - val enumElement = parsed.types[0] as EnumElement - val value = enumElement.constants[0] - assertThat(value.documentation).isEqualTo("Test all...") - } - - @Test - fun syntaxNotRequired() { - val proto = "message Foo {}" - val parsed = ProtoParser.parse(location, proto) - assertThat(parsed.syntax).isNull() - } - - @Test - fun syntaxSpecified() { - val proto = """ - |syntax = "proto3"; - |message Foo {} - """.trimMargin() - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(2, 1), name = "Foo" - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun invalidSyntaxValueThrows() { - val proto = """ - |syntax = "proto4"; - |message Foo {} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage("Syntax error in file.proto at 1:1: unexpected syntax: proto4") - } - } - - @Test - fun syntaxInWrongContextThrows() { - val proto = """ - |message Foo { - | syntax = "proto2"; - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - } catch (e: IllegalStateException) { - assertThat(e).hasMessage("Syntax error in file.proto at 2:3: 'syntax' in MESSAGE") - } - } - - @Test - fun syntaxNotFirstDeclarationThrows() { - val proto = """ - |message Foo {} - |syntax = "proto3"; - """.trimMargin() - try { - ProtoParser.parse(location, proto) - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage( - "Syntax error in file.proto at 2:1: 'syntax' element must be the first declaration in a file" - ) - } - } - - @Test - fun syntaxMayFollowCommentsAndEmptyLines() { - val proto = """ - |/* comment 1 */ - |// comment 2 - | - |syntax = "proto3"; - |message Foo {} - """.trimMargin() - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(5, 1), name = "Foo" - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun proto3MessageFieldsDoNotRequireLabels() { - val proto = """ - |syntax = "proto3"; - |message Message { - | string a = 1; - | int32 b = 2; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(2, 1), name = "Message", fields = listOf( - FieldElement( - location = location.at(3, 3), type = "string", name = "a", tag = 1 - ), FieldElement( - location = location.at(4, 3), type = "int32", name = "b", tag = 2 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun proto3ExtensionFieldsDoNotRequireLabels() { - val proto = """ - |syntax = "proto3"; - |message Message { - |} - |extend Message { - | string a = 1; - | int32 b = 2; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(2, 1), name = "Message" - ) - ), extendDeclarations = listOf( - ExtendElement( - location = location.at(4, 1), name = "Message", fields = listOf( - FieldElement( - location = location.at(5, 3), type = "string", name = "a", tag = 1 - ), FieldElement( - location = location.at(6, 3), type = "int32", name = "b", tag = 2 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun proto3MessageFieldsForbidRequired() { - val proto = """ - |syntax = "proto3"; - |message Message { - | required string a = 1; - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage( - "Syntax error in file.proto at 3:3: 'required' label forbidden in proto3 field declarations" - ) - } - } - - @Test - fun proto3ExtensionFieldsForbidsRequired() { - val proto = """ - |syntax = "proto3"; - |message Message { - |} - |extend Message { - | required string a = 1; - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - Assertions.assertTrue(false) - } catch (expected: IllegalStateException) { - assertThat(expected).hasMessage( - "Syntax error in file.proto at 5:3: 'required' label forbidden in proto3 field declarations" - ) - } - } - - @Test - fun proto3MessageFieldsPermitRepeated() { - val proto = """ - |syntax = "proto3"; - |message Message { - | repeated string a = 1; - |} - """.trimMargin() - - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(2, 1), name = "Message", fields = listOf( - FieldElement( - location = location.at(3, 3), label = REPEATED, type = "string", name = "a", tag = 1 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun proto3ExtensionFieldsPermitRepeated() { - val proto = """ - |syntax = "proto3"; - |message Message { - |} - |extend Message { - | repeated string a = 1; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, syntax = ProtoFile.Syntax.PROTO_3, types = listOf( - MessageElement( - location = location.at(2, 1), name = "Message" - ) - ), extendDeclarations = listOf( - ExtendElement( - location = location.at(4, 1), name = "Message", fields = listOf( - FieldElement( - location = location.at(5, 3), label = REPEATED, type = "string", name = "a", tag = 1 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun parseMessageAndFields() { - val proto = """ - |message SearchRequest { - | required string query = 1; - | optional int32 page_number = 2; - | optional int32 result_per_page = 3; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "SearchRequest", fields = listOf( - FieldElement( - location = location.at(2, 3), label = REQUIRED, type = "string", name = "query", tag = 1 - ), FieldElement( - location = location.at(3, 3), - label = OPTIONAL, - type = "int32", - name = "page_number", - tag = 2 - ), FieldElement( - location = location.at(4, 3), - label = OPTIONAL, - type = "int32", - name = "result_per_page", - tag = 3 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun group() { - val proto = """ - |message SearchResponse { - | repeated group Result = 1 { - | required string url = 2; - | optional string title = 3; - | repeated string snippets = 4; - | } - |} - """.trimMargin() - val message = MessageElement( - location = location.at(1, 1), name = "SearchResponse", groups = listOf( - GroupElement( - location = location.at(2, 3), label = REPEATED, name = "Result", tag = 1, fields = listOf( - FieldElement( - location = location.at(3, 5), label = REQUIRED, type = "string", name = "url", tag = 2 - ), FieldElement( - location = location.at(4, 5), label = OPTIONAL, type = "string", name = "title", tag = 3 - ), FieldElement( - location = location.at(5, 5), label = REPEATED, type = "string", name = "snippets", tag = 4 - ) - ) - ) - ) - ) - val expected = ProtoFileElement( - location = location, types = listOf(message) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun parseMessageAndOneOf() { - val proto = """ - |message SearchRequest { - | required string query = 1; - | oneof page_info { - | int32 page_number = 2; - | int32 result_per_page = 3; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "SearchRequest", fields = listOf( - FieldElement( - location = location.at(2, 3), label = REQUIRED, type = "string", name = "query", tag = 1 - ) - ), oneOfs = listOf( - OneOfElement( - name = "page_info", fields = listOf( - FieldElement( - location = location.at(4, 5), type = "int32", name = "page_number", tag = 2 - ), FieldElement( - location = location.at(5, 5), type = "int32", name = "result_per_page", tag = 3 - ) - ) - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun parseMessageAndOneOfWithGroup() { - val proto = """ - |message SearchRequest { - | required string query = 1; - | oneof page_info { - | int32 page_number = 2; - | group Stuff = 3 { - | optional int32 result_per_page = 4; - | optional int32 page_count = 5; - | } - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "SearchRequest", fields = listOf( - FieldElement( - location = location.at(2, 3), label = REQUIRED, type = "string", name = "query", tag = 1 - ) - ), oneOfs = listOf( - OneOfElement( - name = "page_info", fields = listOf( - FieldElement( - location = location.at(4, 5), type = "int32", name = "page_number", tag = 2 - ) - ), groups = listOf( - GroupElement( - location = location.at(5, 5), name = "Stuff", tag = 3, fields = listOf( - FieldElement( - location = location.at(6, 7), - label = OPTIONAL, - type = "int32", - name = "result_per_page", - tag = 4 - ), FieldElement( - location = location.at(7, 7), - label = OPTIONAL, - type = "int32", - name = "page_count", - tag = 5 - ) - ) - ) - ) - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun parseEnum() { - val proto = """ - |/** - | * What's on my waffles. - | * Also works on pancakes. - | */ - |enum Topping { - | FRUIT = 1; - | /** Yummy, yummy cream. */ - | CREAM = 2; - | - | // Quebec Maple syrup - | SYRUP = 3; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - EnumElement( - location = location.at(5, 1), - name = "Topping", - documentation = "What's on my waffles.\nAlso works on pancakes.", - constants = listOf( - EnumConstantElement( - location = location.at(6, 3), name = "FRUIT", tag = 1 - ), EnumConstantElement( - location = location.at(8, 3), name = "CREAM", tag = 2, documentation = "Yummy, yummy cream." - ), EnumConstantElement( - location = location.at(11, 3), name = "SYRUP", tag = 3, documentation = "Quebec Maple syrup" - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun parseEnumWithOptions() { - val proto = """ - |/** - | * What's on my waffles. - | * Also works on pancakes. - | */ - |enum Topping { - | option(max_choices) = 2; - | - | FRUIT = 1[(healthy) = true]; - | /** Yummy, yummy cream. */ - | CREAM = 2; - | - | // Quebec Maple syrup - | SYRUP = 3; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - EnumElement( - location = location.at(5, 1), - name = "Topping", - documentation = "What's on my waffles.\nAlso works on pancakes.", - options = listOf(OptionElement.create("max_choices", Kind.NUMBER, "2".toBigDecimal(), true)), - constants = listOf( - EnumConstantElement( - location = location.at(8, 3), name = "FRUIT", tag = 1, options = listOf( - OptionElement.create("healthy", Kind.BOOLEAN, true, true) - ) - ), EnumConstantElement( - location = location.at(10, 3), - name = "CREAM", - tag = 2, - documentation = "Yummy, yummy cream." - ), EnumConstantElement( - location = location.at(13, 3), name = "SYRUP", tag = 3, documentation = "Quebec Maple syrup" - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun packageDeclaration() { - val proto = """ - |package google.protobuf; - |option java_package = "com.google.protobuf"; - | - |// The protocol compiler can output a FileDescriptorSet containing the .proto - |// files it parses. - |message FileDescriptorSet { - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, packageName = "google.protobuf", types = listOf( - MessageElement( - location = location.at(6, 1), - name = "FileDescriptorSet", - documentation = "The protocol compiler can output a FileDescriptorSet containing the .proto\nfiles it parses." - ) - ), options = listOf(OptionElement.create("java_package", Kind.STRING, "com.google.protobuf")) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun extensionsWithOptions() { - // Modern descriptor.proto declares extension-range options, e.g. "[declaration = {...}]". These are - // descriptor-internal metadata that the parser must accept (and discard) on both single tags and ranges. - val proto = """ - |message MessageOptions { - | extensions 536000000 [declaration = { - | number: 536000000 - | type: ".buf.descriptor.v1.FileDescriptorSetExtension" - | full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension" - | }]; - | extensions 1000 to max [ - | declaration = { number: 1000, full_name: ".pb.cpp", type: ".pb.CppFeatures" }, - | declaration = { number: 1001, full_name: ".pb.java", type: ".pb.JavaFeatures" } - | ]; - |} - """.trimMargin() - val messageElement = MessageElement( - location = location.at(1, 1), - name = "MessageOptions", - extensions = listOf( - ExtensionsElement(location.at(2, 3), "", 536000000, 536000000), - ExtensionsElement(location.at(7, 3), "", 1000, Util.MAX_TAG_VALUE) - ) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - val actual = ProtoParser.parse(location, proto) - assertThat(actual).isEqualTo(expected) - } - - @Test - fun nestingInMessage() { - val proto = """ - |message FieldOptions { - | optional CType ctype = 1[old_default = STRING, deprecated = true]; - | enum CType { - | STRING = 0[(opt_a) = 1, (opt_b) = 2]; - | }; - | // Clients can define custom options in extensions of this message. See above. - | extensions 500; - | extensions 1000 to max; - |} - """.trimMargin() - val enumElement = EnumElement( - location = location.at(3, 3), name = "CType", constants = listOf( - EnumConstantElement( - location = location.at(4, 5), name = "STRING", tag = 0, options = listOf( - OptionElement.create("opt_a", Kind.NUMBER, "1".toBigDecimal(), true), - OptionElement.create("opt_b", Kind.NUMBER, "2".toBigDecimal(), true) - ) - ) - ) - ) - val field = FieldElement( - location = location.at(2, 3), label = OPTIONAL, type = "CType", name = "ctype", tag = 1, options = listOf( - OptionElement.create("old_default", Kind.ENUM, "STRING"), - OptionElement.create("deprecated", Kind.BOOLEAN, true) - ) - ) - assertThat(field.options).containsOnly( - OptionElement.create("old_default", Kind.ENUM, "STRING"), - OptionElement.create("deprecated", Kind.BOOLEAN, true) - ) - - val messageElement = MessageElement( - location = location.at(1, 1), - name = "FieldOptions", - fields = listOf(field), - nestedTypes = listOf(enumElement), - extensions = listOf( - ExtensionsElement( - location = location.at(7, 3), - documentation = "Clients can define custom options in extensions of this message. See above.", - start = 500, - end = 500 - ), ExtensionsElement(location.at(8, 3), "", 1000, Util.MAX_TAG_VALUE) - ) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - val actual = ProtoParser.parse(location, proto) - assertThat(actual).isEqualTo(expected) - } - - @Test - fun optionParentheses() { - val proto = """ - |message Chickens { - | optional bool koka_ko_koka_ko = 1[old_default = true]; - | optional bool coodle_doodle_do = 2[(delay) = 100, old_default = false]; - | optional bool coo_coo_ca_cha = 3[old_default = true, (delay) = 200]; - | optional bool cha_chee_cha = 4; - |} - """.trimMargin() - - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "Chickens", fields = listOf( - FieldElement( - location = location.at(2, 3), - label = OPTIONAL, - type = "bool", - name = "koka_ko_koka_ko", - tag = 1, - options = listOf( - OptionElement.create("old_default", Kind.BOOLEAN, true) - ) - ), FieldElement( - location = location.at(3, 3), - label = OPTIONAL, - type = "bool", - name = "coodle_doodle_do", - tag = 2, - options = listOf( - OptionElement.create("delay", Kind.NUMBER, "100".toBigDecimal(), true), - OptionElement.create("old_default", Kind.BOOLEAN, false) - ) - ), FieldElement( - location = location.at(4, 3), - label = OPTIONAL, - type = "bool", - name = "coo_coo_ca_cha", - tag = 3, - options = listOf( - OptionElement.create("old_default", Kind.BOOLEAN, true), - OptionElement.create("delay", Kind.NUMBER, "200".toBigDecimal(), true) - ) - ), FieldElement( - location = location.at(5, 3), - label = OPTIONAL, - type = "bool", - name = "cha_chee_cha", - tag = 4 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun imports() { - val proto = "import \"src/test/resources/unittest_import.proto\";\n" - val expected = ProtoFileElement( - location = location, imports = listOf("src/test/resources/unittest_import.proto") - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun publicImports() { - val proto = "import public \"src/test/resources/unittest_import.proto\";\n" - val expected = ProtoFileElement( - location = location, publicImports = listOf("src/test/resources/unittest_import.proto") - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun extend() { - val proto = """ - |// Extends Foo - |extend Foo { - | optional int32 bar = 126; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, extendDeclarations = listOf( - ExtendElement( - location = location.at(2, 1), name = "Foo", documentation = "Extends Foo", fields = listOf( - FieldElement( - location = location.at(3, 3), label = OPTIONAL, type = "int32", name = "bar", tag = 126 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun extendInMessage() { - val proto = """ - |message Bar { - | extend Foo { - | optional Bar bar = 126; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, - types = listOf(MessageElement(location = location.at(1, 1), name = "Bar")), - extendDeclarations = listOf( - ExtendElement( - location = location.at(2, 3), name = "Foo", fields = listOf( - FieldElement( - location = location.at(3, 5), label = OPTIONAL, type = "Bar", name = "bar", tag = 126 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun extendInMessageWithPackage() { - val proto = """ - |package kit.kat; - | - |message Bar { - | extend Foo { - | optional Bar bar = 126; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, packageName = "kit.kat", types = listOf( - MessageElement(location = location.at(3, 1), name = "Bar") - - ), extendDeclarations = listOf( - ExtendElement( - location = location.at(4, 3), name = "Foo", fields = listOf( - FieldElement( - location = location.at(5, 5), label = OPTIONAL, type = "Bar", name = "bar", tag = 126 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun fqcnExtendInMessage() { - val proto = """ - |message Bar { - | extend example.Foo { - | optional Bar bar = 126; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, - types = listOf(MessageElement(location = location.at(1, 1), name = "Bar")), - extendDeclarations = listOf( - ExtendElement( - location = location.at(2, 3), name = "example.Foo", fields = listOf( - FieldElement( - location = location.at(3, 5), label = OPTIONAL, type = "Bar", name = "bar", tag = 126 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun fqcnExtendInMessageWithPackage() { - val proto = """ - |package kit.kat; - | - |message Bar { - | extend example.Foo { - | optional Bar bar = 126; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, packageName = "kit.kat", types = listOf( - MessageElement(location = location.at(3, 1), name = "Bar") - - ), extendDeclarations = listOf( - ExtendElement( - location = location.at(4, 3), name = "example.Foo", fields = listOf( - FieldElement( - location = location.at(5, 5), label = OPTIONAL, type = "Bar", name = "bar", tag = 126 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun defaultFieldWithParen() { - val proto = """ - |message Foo { - | optional string claim_token = 2[(squareup.redacted) = true]; - |} - """.trimMargin() - val field = FieldElement( - location = location.at(2, 3), - label = OPTIONAL, - type = "string", - name = "claim_token", - tag = 2, - options = listOf(OptionElement.create("squareup.redacted", Kind.BOOLEAN, true, true)) - ) - assertThat(field.options).containsOnly(OptionElement.create("squareup.redacted", Kind.BOOLEAN, true, true)) - - val messageElement = MessageElement( - location = location.at(1, 1), name = "Foo", fields = listOf(field) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - // Parse \a, \b, \f, \n, \r, \t, \v, \[0-7]{1-3}, and \[xX]{0-9a-fA-F]{1,2} - @Test - fun defaultFieldWithStringEscapes() { - val proto = """ - |message Foo { - | optional string name = 1 [ - | x = "\a\b\f\n\r\t\v\1f\01\001\11\011\111\xe\Xe\xE\xE\x41\X41" - | ]; - |} - """.trimMargin() - val field = FieldElement( - location = location.at(2, 3), label = OPTIONAL, type = "string", name = "name", tag = 1, options = listOf( - OptionElement.create( - "x", - Kind.STRING, - "\u0007\b\u000C\n\r\t\u000b\u0001f\u0001\u0001\u0009\u0009I\u000e\u000e\u000e\u000eAA" - ) - ) - ) - assertThat(field.options).containsOnly( - OptionElement.create( - "x", - Kind.STRING, - "\u0007\b\u000C\n\r\t\u000b\u0001f\u0001\u0001\u0009\u0009I\u000e\u000e\u000e\u000eAA" - ) - ) - - val messageElement = MessageElement( - location = location.at(1, 1), name = "Foo", fields = listOf(field) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun stringWithSingleQuotes() { - val proto = """ - |message Foo { - | optional string name = 1[default = 'single"quotes']; - |} - """.trimMargin() - - val field = FieldElement( - location = location.at(2, 3), - label = OPTIONAL, - type = "string", - name = "name", - tag = 1, - defaultValue = "single\"quotes" - ) - val messageElement = MessageElement( - location = location.at(1, 1), name = "Foo", fields = listOf(field) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun adjacentStringsConcatenated() { - val proto = """ - |message Foo { - | optional string name = 1 [ - | default = "concat " - | 'these ' - | "please" - | ]; - |} - """.trimMargin() - - val field = FieldElement( - location = location.at(2, 3), - label = OPTIONAL, - type = "string", - name = "name", - tag = 1, - defaultValue = "concat these please" - ) - val messageElement = MessageElement( - location = location.at(1, 1), name = "Foo", fields = listOf(field) - ) - val expected = ProtoFileElement( - location = location, types = listOf(messageElement) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun invalidHexStringEscape() { - val proto = """ - |message Foo { - | optional string name = 1 [default = "\xW"]; - |} - """.trimMargin() - try { - ProtoParser.parse(location, proto) - Assertions.assertTrue(false) - } catch (e: IllegalStateException) { - assertThat(e.message!!.contains("expected a digit after \\x or \\X")) - } - } - - @Test - fun service() { - val proto = """ - |service SearchService { - | option (default_timeout) = 30; - | - | rpc Search (SearchRequest) returns (SearchResponse); - | rpc Purchase (PurchaseRequest) returns (PurchaseResponse) { - | option (squareup.sake.timeout) = 15; - | option (squareup.a.b) = { - | value: [ - | FOO, - | BAR - | ] - | }; - | } - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, services = listOf( - ServiceElement( - location = location.at(1, 1), name = "SearchService", options = listOf( - OptionElement.create("default_timeout", Kind.NUMBER, "30".toBigDecimal(), true) - ), rpcs = listOf( - RpcElement( - location = location.at(4, 3), - name = "Search", - requestType = "SearchRequest", - responseType = "SearchResponse" - ), RpcElement( - location = location.at(5, 3), - name = "Purchase", - requestType = "PurchaseRequest", - responseType = "PurchaseResponse", - options = listOf( - OptionElement.create("squareup.sake.timeout", Kind.NUMBER, "15".toBigDecimal(), true), - OptionElement.create( - "squareup.a.b", Kind.MAP, mapOf("value" to listOf("FOO", "BAR")), true - ) - ) - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun streamingService() { - val proto = """ - |service RouteGuide { - | rpc GetFeature (Point) returns (Feature) {} - | rpc ListFeatures (Rectangle) returns (stream Feature) {} - | rpc RecordRoute (stream Point) returns (RouteSummary) {} - | rpc RouteChat (stream RouteNote) returns (stream RouteNote) {} - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, services = listOf( - ServiceElement( - location = location.at(1, 1), name = "RouteGuide", rpcs = listOf( - RpcElement( - location = location.at(2, 3), - name = "GetFeature", - requestType = "Point", - responseType = "Feature" - ), RpcElement( - location = location.at(3, 3), - name = "ListFeatures", - requestType = "Rectangle", - responseType = "Feature", - responseStreaming = true - ), RpcElement( - location = location.at(4, 3), - name = "RecordRoute", - requestType = "Point", - responseType = "RouteSummary", - requestStreaming = true - ), RpcElement( - location = location.at(5, 3), - name = "RouteChat", - requestType = "RouteNote", - responseType = "RouteNote", - requestStreaming = true, - responseStreaming = true - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun hexTag() { - val proto = """ - |message HexTag { - | required string hex = 0x10; - | required string uppercase_x_hex = 0X11; - |} - """.trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "HexTag", fields = listOf( - FieldElement( - location = location.at(2, 3), label = REQUIRED, type = "string", name = "hex", tag = 16 - ), FieldElement( - location = location.at(3, 3), - label = REQUIRED, - type = "string", - name = "uppercase_x_hex", - tag = 17 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun structuredOption() { - val proto = """ - |message ExoticOptions { - | option (squareup.one) = {name: "Name", class_name:"ClassName"}; - | option (squareup.two.a) = {[squareup.options.type]: EXOTIC}; - | option (squareup.two.b) = {names: ["Foo", "Bar"]}; - | option (squareup.three) = {x: {y: "1" y: 2 } }; // NOTE: Omitted optional comma - | option (squareup.four) = {x: {y: {z: "1" }, y: {z: 2 }}}; - |} - """.trimMargin() - - val option_one_map = LinkedHashMap() - option_one_map["name"] = "Name" - option_one_map["class_name"] = "ClassName" - val option_two_a_map = LinkedHashMap() - option_two_a_map["[squareup.options.type]"] = "EXOTIC" - val option_two_b_map = LinkedHashMap>() - option_two_b_map["names"] = Arrays.asList("Foo", "Bar") - val option_three_map = LinkedHashMap>() - val option_three_nested_map = LinkedHashMap() - option_three_nested_map["y"] = Arrays.asList("1", "2".toBigDecimal()) - option_three_map["x"] = option_three_nested_map - - val option_four_map = LinkedHashMap>() - val option_four_map_1 = LinkedHashMap() - val option_four_map_2_a = LinkedHashMap() - option_four_map_2_a["z"] = "1" - val option_four_map_2_b = LinkedHashMap() - option_four_map_2_b["z"] = "2".toBigDecimal() - option_four_map_1["y"] = listOf(option_four_map_2_a, option_four_map_2_b) - option_four_map["x"] = option_four_map_1 - - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "ExoticOptions", options = listOf( - OptionElement.create("squareup.one", Kind.MAP, option_one_map, true), OptionElement.create( - "squareup.two.a", Kind.MAP, option_two_a_map, true - ), OptionElement.create( - "squareup.two.b", Kind.MAP, option_two_b_map, true - ), OptionElement.create( - "squareup.three", Kind.MAP, option_three_map, true - ), OptionElement.create("squareup.four", Kind.MAP, option_four_map, true) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun optionsWithNestedMapsAndTrailingCommas() { - val proto = """ - |message StructuredOption { - | optional field.type has_options = 3 [ - | (option_map) = { - | nested_map: {key:"value", key2:["value2a","value2b"]}, - | }, - | (option_string) = ["string1","string2"] - | ]; - |} - """.trimMargin() - val field = FieldElement( - location = location.at(2, 5), - label = OPTIONAL, - type = "field.type", - name = "has_options", - tag = 3, - options = listOf( - OptionElement.create( - "option_map", Kind.MAP, mapOf( - "nested_map" to mapOf("key" to "value", "key2" to listOf("value2a", "value2b")) - ), true - ), OptionElement.create("option_string", Kind.LIST, listOf("string1", "string2"), true) - ) - ) - assertThat(field.options).containsOnly( - OptionElement.create( - "option_map", Kind.MAP, mapOf( - "nested_map" to mapOf("key" to "value", "key2" to listOf("value2a", "value2b")) - ), true - ), OptionElement.create("option_string", Kind.LIST, listOf("string1", "string2"), true) - ) - - val expected = MessageElement( - location = location.at(1, 1), name = "StructuredOption", fields = listOf(field) - ) - val protoFile = ProtoFileElement( - location = location, types = listOf(expected) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(protoFile) - } - - @Test - fun optionNumericalBounds() { - val proto = """ - |message Test { - | optional int32 default_int32 = 401 [x = 2147483647]; - | optional uint32 default_uint32 = 402 [x = 4294967295]; - | optional sint32 default_sint32 = 403 [x = -2147483648]; - | optional fixed32 default_fixed32 = 404 [x = 4294967295]; - | optional sfixed32 default_sfixed32 = 405 [x = -2147483648]; - | optional int64 default_int64 = 406 [x = 9223372036854775807]; - | optional uint64 default_uint64 = 407 [x = 18446744073709551615]; - | optional sint64 default_sint64 = 408 [x = -9223372036854775808]; - | optional fixed64 default_fixed64 = 409 [x = 18446744073709551615]; - | optional sfixed64 default_sfixed64 = 410 [x = -9223372036854775808]; - | optional bool default_bool = 411 [x = true]; - | optional float default_float = 412 [x = 123.456e7]; - | optional double default_double = 413 [x = 123.456e78]; - | optional string default_string = 414 [x = "çok\a\b\f\n\r\t\v\1\01\001\17\017\176\x1\x01\x11\X1\X01\X11güzel" ]; - | optional bytes default_bytes = 415 [x = "çok\a\b\f\n\r\t\v\1\01\001\17\017\176\x1\x01\x11\X1\X01\X11güzel" ]; - | optional NestedEnum default_nested_enum = 416 [x = A ]; - |}""".trimMargin() - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "Test", fields = listOf( - FieldElement( - location = location.at(2, 3), - label = OPTIONAL, - type = "int32", - name = "default_int32", - tag = 401, - options = listOf(OptionElement.create("x", Kind.NUMBER, "2147483647".toBigDecimal())) - ), FieldElement( - location = location.at(3, 3), - label = OPTIONAL, - type = "uint32", - name = "default_uint32", - tag = 402, - options = listOf(OptionElement.create("x", Kind.NUMBER, "4294967295".toBigDecimal())) - ), FieldElement( - location = location.at(4, 3), - label = OPTIONAL, - type = "sint32", - name = "default_sint32", - tag = 403, - options = listOf(OptionElement.create("x", Kind.NUMBER, "-2147483648".toBigDecimal())) - ), FieldElement( - location = location.at(5, 3), - label = OPTIONAL, - type = "fixed32", - name = "default_fixed32", - tag = 404, - options = listOf(OptionElement.create("x", Kind.NUMBER, "4294967295".toBigDecimal())) - ), FieldElement( - location = location.at(6, 3), - label = OPTIONAL, - type = "sfixed32", - name = "default_sfixed32", - tag = 405, - options = listOf(OptionElement.create("x", Kind.NUMBER, "-2147483648".toBigDecimal())) - ), FieldElement( - location = location.at(7, 3), - label = OPTIONAL, - type = "int64", - name = "default_int64", - tag = 406, - options = listOf( - OptionElement.create("x", Kind.NUMBER, "9223372036854775807".toBigDecimal()) - ) - ), FieldElement( - location = location.at(8, 3), - label = OPTIONAL, - type = "uint64", - name = "default_uint64", - tag = 407, - options = listOf( - OptionElement.create("x", Kind.NUMBER, "18446744073709551615".toBigDecimal()) - ) - ), FieldElement( - location = location.at(9, 3), - label = OPTIONAL, - type = "sint64", - name = "default_sint64", - tag = 408, - options = listOf( - OptionElement.create("x", Kind.NUMBER, "-9223372036854775808".toBigDecimal()) - ) - ), FieldElement( - location = location.at(10, 3), - label = OPTIONAL, - type = "fixed64", - name = "default_fixed64", - tag = 409, - options = listOf( - OptionElement.create("x", Kind.NUMBER, "18446744073709551615".toBigDecimal()) - ) - ), FieldElement( - location = location.at(11, 3), - label = OPTIONAL, - type = "sfixed64", - name = "default_sfixed64", - tag = 410, - options = listOf( - OptionElement.create("x", Kind.NUMBER, "-9223372036854775808".toBigDecimal()) - ) - ), FieldElement( - location = location.at(12, 3), - label = OPTIONAL, - type = "bool", - name = "default_bool", - tag = 411, - options = listOf(OptionElement.create("x", Kind.BOOLEAN, true)) - ), FieldElement( - location = location.at(13, 3), - label = OPTIONAL, - type = "float", - name = "default_float", - tag = 412, - options = listOf(OptionElement.create("x", Kind.NUMBER, "123.456e7".toBigDecimal())) - ), FieldElement( - location = location.at(14, 3), - label = OPTIONAL, - type = "double", - name = "default_double", - tag = 413, - options = listOf(OptionElement.create("x", Kind.NUMBER, "123.456e78".toBigDecimal())) - ), FieldElement( - location = location.at(15, 3), - label = OPTIONAL, - type = "string", - name = "default_string", - tag = 414, - options = listOf( - OptionElement.create( - "x", - Kind.STRING, - "çok\u0007\b\u000C\n\r\t\u000b\u0001\u0001\u0001\u000f\u000f~\u0001\u0001\u0011\u0001\u0001\u0011güzel" - ) - ) - ), FieldElement( - location = location.at(17, 3), - label = OPTIONAL, - type = "bytes", - name = "default_bytes", - tag = 415, - options = listOf( - OptionElement.create( - "x", - Kind.STRING, - "çok\u0007\b\u000C\n\r\t\u000b\u0001\u0001\u0001\u000f\u000f~\u0001\u0001\u0011\u0001\u0001\u0011güzel" - ) - ) - ), FieldElement( - location = location.at(19, 3), - label = OPTIONAL, - type = "NestedEnum", - name = "default_nested_enum", - tag = 416, - options = listOf(OptionElement.create("x", Kind.ENUM, "A")) - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun extensionWithNestedMessage() { - val proto = """ - |message Foo { - | optional int32 bar = 1[ - | (validation.range).min = 1, - | (validation.range).max = 100, - | old_default = 20 - | ]; - |} - """.trimMargin() - val field = FieldElement( - location = location.at(2, 3), label = OPTIONAL, type = "int32", name = "bar", tag = 1, options = listOf( - OptionElement.create( - "validation.range", Kind.OPTION, OptionElement.create("min", Kind.NUMBER, "1".toBigDecimal()), true - ), OptionElement.create( - "validation.range", - Kind.OPTION, - OptionElement.create("max", Kind.NUMBER, "100".toBigDecimal()), - true - ), OptionElement.create("old_default", Kind.NUMBER, "20".toBigDecimal()) - ) - ) - assertThat(field.options).containsOnly( - OptionElement.create( - "validation.range", Kind.OPTION, OptionElement.create("min", Kind.NUMBER, "1".toBigDecimal()), true - ), OptionElement.create( - "validation.range", - Kind.OPTION, - OptionElement.create("max", Kind.NUMBER, "100".toBigDecimal()), - true - ), OptionElement.create("old_default", Kind.NUMBER, "20".toBigDecimal()) - ) - - val expected = MessageElement( - location = location.at(1, 1), name = "Foo", fields = listOf(field) - ) - val protoFile = ProtoFileElement( - location = location, types = listOf(expected) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(protoFile) - } - - @Test - fun reserved() { - val proto = """ - |message Foo { - | reserved 10, 12 to 14, 'foo'; - |} - """.trimMargin() - val message = MessageElement( - location = location.at(1, 1), name = "Foo", reserveds = listOf( - ReservedElement( - location = location.at(2, 3), values = listOf(10, Range.closed(12, 14), "foo") - ) - ) - ) - val expected = ProtoFileElement( - location = location, types = listOf(message) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun reservedWithMaxKeyword() { - val proto = """ - |message Foo { - | reserved 10 to max; - |} - """.trimMargin() - val message = MessageElement( - location = location.at(1, 1), name = "Foo", reserveds = listOf( - ReservedElement( - location = location.at(2, 3), values = listOf(Range.closed(10, Util.MAX_TAG_VALUE)) - ) - ) - ) - val expected = ProtoFileElement( - location = location, types = listOf(message) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } - - @Test - fun noWhitespace() { - val proto = "message C {optional A.B ab = 1;}" - val expected = ProtoFileElement( - location = location, types = listOf( - MessageElement( - location = location.at(1, 1), name = "C", fields = listOf( - FieldElement( - location = location.at(1, 12), label = OPTIONAL, type = "A.B", name = "ab", tag = 1 - ) - ) - ) - ) - ) - assertThat(ProtoParser.parse(location, proto)).isEqualTo(expected) - } -} diff --git a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ServiceElementTest.kt b/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ServiceElementTest.kt deleted file mode 100644 index be89fd2f..00000000 --- a/schema2proto-wire/src/test/java/com/squareup/wire/schema/internal/parser/ServiceElementTest.kt +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2014 Square, 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.squareup.wire.schema.internal.parser - -import com.squareup.wire.schema.Location -import com.squareup.wire.schema.internal.parser.OptionElement.Kind -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - - -class ServiceElementTest { - internal var location = Location.get("file.proto") - - @Test - fun emptyToSchema() { - val service = ServiceElement( - location = location, - name = "Service" - ) - val expected = "service Service {}\n" - assertThat(service.toSchema()).isEqualTo(expected) - } - - @Test - fun singleToSchema() { - val service = ServiceElement( - location = location, - name = "Service", - rpcs = listOf( - RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - ) - ) - val expected = """ - |service Service { - | rpc Name (RequestType) returns (ResponseType); - |} - |""".trimMargin() - assertThat(service.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleRpcs() { - val firstName = RpcElement( - location = location, - name = "FirstName", - requestType = "RequestType", - responseType = "ResponseType" - ) - val lastName = RpcElement( - location = location, - name = "LastName", - requestType = "RequestType", - responseType = "ResponseType" - ) - val service = ServiceElement( - location = location, - name = "Service", - rpcs = listOf(firstName, lastName) - ) - assertThat(service.rpcs).hasSize(2) - } - - @Test - fun singleWithOptionsToSchema() { - val service = ServiceElement( - location = location, - name = "Service", - options = listOf(OptionElement.create("foo", Kind.STRING, "bar")), - rpcs = listOf( - RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - ) - ) - val expected = """ - |service Service { - | option foo = "bar"; - | - | rpc Name (RequestType) returns (ResponseType); - |} - |""".trimMargin() - assertThat(service.toSchema()).isEqualTo(expected) - } - - @Test - fun addMultipleOptions() { - val kitKat = OptionElement.create("kit", Kind.STRING, "kat") - val fooBar = OptionElement.create("foo", Kind.STRING, "bar") - val service = ServiceElement( - location = location, - name = "Service", - options = listOf(kitKat, fooBar), - rpcs = listOf( - RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - ) - ) - assertThat(service.options).hasSize(2) - } - - @Test - fun singleWithDocumentationToSchema() { - val service = ServiceElement( - location = location, - name = "Service", - documentation = "Hello", - rpcs = listOf( - RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - ) - ) - val expected = """ - |// Hello - |service Service { - | rpc Name (RequestType) returns (ResponseType); - |} - |""".trimMargin() - assertThat(service.toSchema()).isEqualTo(expected) - } - - @Test - fun multipleToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - val service = ServiceElement( - location = location, - name = "Service", - rpcs = listOf(rpc, rpc) - ) - val expected = """ - |service Service { - | rpc Name (RequestType) returns (ResponseType); - | rpc Name (RequestType) returns (ResponseType); - |} - |""".trimMargin() - - assertThat(service.toSchema()).isEqualTo(expected) - } - - @Test - fun rpcToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType" - ) - val expected = "rpc Name (RequestType) returns (ResponseType);\n" - assertThat(rpc.toSchema()).isEqualTo(expected) - } - - @Test - fun rpcWithDocumentationToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - documentation = "Hello", - requestType = "RequestType", - responseType = "ResponseType" - ) - val expected = """ - |// Hello - |rpc Name (RequestType) returns (ResponseType); - |""".trimMargin() - assertThat(rpc.toSchema()).isEqualTo(expected) - } - - @Test - fun rpcWithOptionsToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType", - options = listOf(OptionElement.create("foo", Kind.STRING, "bar")) - ) - - val expected = """ - |rpc Name (RequestType) returns (ResponseType) { - | option foo = "bar"; - |}; - |""".trimMargin() - assertThat(rpc.toSchema()).isEqualTo(expected) - } - - @Test - fun rpcWithRequestStreamingToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType", - requestStreaming = true - ) - val expected = "rpc Name (stream RequestType) returns (ResponseType);\n" - assertThat(rpc.toSchema()).isEqualTo(expected) - } - - @Test - fun rpcWithResponseStreamingToSchema() { - val rpc = RpcElement( - location = location, - name = "Name", - requestType = "RequestType", - responseType = "ResponseType", - responseStreaming = true - ) - val expected = "rpc Name (RequestType) returns (stream ResponseType);\n" - assertThat(rpc.toSchema()).isEqualTo(expected) - } -}