// // Copyright © 2014, David Tesler (https://github.com/protobufel) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the <organization> nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // package com.github.protobufel.test.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.google.protobuf.DescriptorProtos.DescriptorProto; import com.google.protobuf.DescriptorProtos.EnumDescriptorProto; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Label; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type; import com.google.protobuf.DescriptorProtos.FieldOptions; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.FileDescriptorSet; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.UnknownFieldSet; public class DescriptorFactory { private final FileDescriptorProto.Builder fileBuilder; private DescriptorFactory() { this(FileDescriptorProto.getDefaultInstance()); } private DescriptorFactory(FileDescriptorProto prototype) { this.fileBuilder = FileDescriptorProto.newBuilder(prototype); } private Descriptor findDescriptor(final FileDescriptor fileDescriptor, final String messageTypeName) { if ((messageTypeName == null) || (fileDescriptor == null)) { throw new NullPointerException(); } for (Descriptor descriptor : fileDescriptor.getMessageTypes()) { if (messageTypeName.equals(descriptor.getFullName())) { return descriptor; } } for (Descriptor descriptor : fileDescriptor.getMessageTypes()) { findDescriptor(descriptor, messageTypeName); } return null; } private Descriptor findDescriptor(Descriptor descriptor, final String messageTypeName) { for (Descriptor child : descriptor.getNestedTypes()) { if (messageTypeName.equals(child.getFullName())) { return descriptor; } } for (Descriptor child : descriptor.getNestedTypes()) { final Descriptor foundDescriptor = findDescriptor(child, messageTypeName); if (foundDescriptor != null) { return foundDescriptor; } } return null; } private FileDescriptor createFileDescriptor(FileDescriptorProto fileProto, FileDescriptor[] dependencies) throws DescriptorValidationException { return FileDescriptor.buildFrom(fileProto, dependencies); } private FileDescriptorProto createFileDescriptorProto(String fileName, String packageName, UnknownFieldSet unknownFields) { FileDescriptorProto.Builder fileBuilder = FileDescriptorProto.newBuilder(); return fileBuilder .setName(fileName) .setPackage(packageName) .setUnknownFields(unknownFields) .addAllDependency(Collections.<String>emptyList()) .addAllEnumType(Collections.<EnumDescriptorProto>emptyList()) .addAllExtension(Collections.<FieldDescriptorProto>emptyList()) .addAllMessageType(Collections.<DescriptorProto>emptyList()) .addAllPublicDependency(Collections.<Integer>emptyList()) .addAllService(Collections.<ServiceDescriptorProto>emptyList()) .build(); } private DescriptorProto createMessageDescriptorProto(String messageName, UnknownFieldSet unknownFields) { DescriptorProto.Builder messageBuilder = DescriptorProto.newBuilder(); return messageBuilder .setName(messageName) .setUnknownFields(unknownFields) .addAllEnumType(Collections.<EnumDescriptorProto>emptyList()) .addAllExtension(Collections.<FieldDescriptorProto>emptyList()) .addAllField(Collections.<FieldDescriptorProto>emptyList()) .addAllNestedType(Collections.<DescriptorProto>emptyList()) .build(); } private FieldDescriptorProto createFieldDescriptorProto(String name, int index, Type type, String typeName, Label label, String defaultValue, String extendee, UnknownFieldSet unknownFields, FieldOptions options) { FieldDescriptorProto.Builder fieldBuilder = FieldDescriptorProto.newBuilder(); return fieldBuilder .setName(name) .setNumber(index) .setType(type) .setTypeName(typeName) .setLabel(label) .setDefaultValue(defaultValue) .setExtendee(extendee) .setUnknownFields(unknownFields) .setOptions(options) .build(); } public static final class FileDescriptorSetBuilder { private final Set<FileDescriptorProto> fileProtoSet; private FileDescriptorSetBuilder() { this(new LinkedHashSet<FileDescriptorProto>()); } private FileDescriptorSetBuilder(LinkedHashSet<FileDescriptorProto> fileProtoSet) { this.fileProtoSet = fileProtoSet; } public static FileDescriptorSetBuilder newBuilder() { return new FileDescriptorSetBuilder(); } public static FileDescriptorSetBuilder newBuilder(final FileDescriptorSet fileDescriptorSet) { final FileDescriptorSetBuilder fileDescriptorSetBuilder = new FileDescriptorSetBuilder(); fileDescriptorSetBuilder.fileProtoSet.addAll(fileDescriptorSet.getFileList()); return fileDescriptorSetBuilder; } public FileDescriptorSetBuilder from(final FileDescriptorSet fileDescriptorSet) { fileProtoSet.clear(); fileProtoSet.addAll(fileDescriptorSet.getFileList()); return this; } public FileDescriptorSetBuilder mergeFrom(final FileDescriptorSet fileDescriptorSet) { fileProtoSet.addAll(fileDescriptorSet.getFileList()); return this; } public FileDescriptorSetBuilder addFile(final FileDescriptor file) { if (!fileProtoSet.add(file.toProto())) { return this; } for (FileDescriptor dependency : file.getDependencies()) { addFile(dependency); } return this; } public FileDescriptorSetBuilder addDescriptor(final Descriptor descriptor) { return addFile(descriptor.getFile()); } public FileDescriptorSetBuilder addAllFiles(final Iterable<FileDescriptor> fileDescriptors) { for (FileDescriptor fileDescriptor : fileDescriptors) { addFile(fileDescriptor); } return this; } public FileDescriptorSetBuilder addAllDescriptors(final Iterable<Descriptor> descriptors) { for (Descriptor descriptor : descriptors) { addDescriptor(descriptor); } return this; } public FileDescriptorSet build() { return FileDescriptorSet.newBuilder().addAllFile(fileProtoSet).build(); } } public static final class FileDescriptorCache { private final Map<String, Descriptor> descriptorCache; private final Map<String, FileDescriptor> fileCache; private FileDescriptorCache(final FileDescriptorSet fileDescriptorSet) { if (fileDescriptorSet == null) { throw new NullPointerException(); } this.fileCache = new LinkedHashMap<String, FileDescriptor>(buildFilesFrom(fileDescriptorSet)); this.descriptorCache = new HashMap<String, Descriptor>(); updateDecsriptorCache(); } public static FileDescriptorCache buildFrom(final FileDescriptorSet fileDescriptorSet) { return new FileDescriptorCache(fileDescriptorSet); } public Descriptor getDescriptor(String descriptorFullName) { if ((descriptorFullName == null) || descriptorFullName.isEmpty()) { throw new NullPointerException("descriptorFullName is null or empty"); } return descriptorCache.get(descriptorFullName); } public FileDescriptor getFileDescriptor(String fileName) { if ((fileName == null) || fileName.isEmpty()) { throw new NullPointerException("fileName is null or empty"); } return fileCache.get(fileName); } private void updateDecsriptorCache() { for (FileDescriptor file : fileCache.values()) { for (Descriptor descriptor : file.getMessageTypes()) { updateDecsriptorCache(descriptor); } } } private void updateDecsriptorCache(Descriptor descriptor) { descriptorCache.put(descriptor.getFullName(), descriptor); for (Descriptor child : descriptor.getNestedTypes()) { updateDecsriptorCache(child); } } private Map<String, FileDescriptor> buildFilesFrom( final FileDescriptorSet fileDescriptorSet) { final Map<String, FileDescriptor> fileMap = new LinkedHashMap<String, FileDescriptor>(); final LinkedList<FileDescriptorProto> fileProtos = new LinkedList<FileDescriptorProto>(); final FileDescriptor[] emptyDependencies = new FileDescriptor[0]; try { //find all leaves for (FileDescriptorProto fileProto : fileDescriptorSet.getFileList()) { if (fileProto.getDependencyCount() == 0) { fileMap.put(fileProto.getName(), FileDescriptor.buildFrom(fileProto, emptyDependencies)); } else { fileProtos.add(fileProto); } } while (!fileProtos.isEmpty()) { //find all fileProto(-s) with only dependencies from the files and make them into files boolean isLeafFound = false; for (Iterator<FileDescriptorProto> iterator = fileProtos.descendingIterator(); iterator.hasNext();) { FileDescriptorProto fileProto = iterator.next(); if (fileMap.keySet().containsAll(fileProto.getDependencyList())) { final FileDescriptor[] dependencies = new FileDescriptor[fileProto.getDependencyCount()]; int i = 0; for (String fileName : fileProto.getDependencyList()) { dependencies[i++] = fileMap.get(fileName); } fileMap.put(fileProto.getName(), FileDescriptor.buildFrom(fileProto, dependencies)); iterator.remove(); isLeafFound = true; } } if (!isLeafFound) { List<String> nameList = new ArrayList<String>(fileProtos.size()); for (FileDescriptorProto fileProto : fileProtos) { nameList.add(fileProto.getName()); } throw new RuntimeException(String.format( "these FileDescriprorProtos are circular or refer to nonexisting files: %s", nameList)); } } return fileMap; } catch (DescriptorValidationException e) { throw new RuntimeException(e); } } } }