1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import com.google.protobuf.Descriptors.FieldDescriptor; 34 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Map.Entry; 41 42 43 /** 44 * Data structure which is populated with the locations of each field value parsed from the text. 45 * 46 * <p>The locations of primary fields values are retrieved by {@code getLocation} or 47 * {@code getLocations}. The locations of sub message values are within nested 48 * {@code TextFormatParseInfoTree}s and are retrieve by {@code getNestedTree} or {@code getNestedTrees}. 49 * 50 * <p>The {@code TextFormatParseInfoTree} is created by a Builder. 51 */ 52 public class TextFormatParseInfoTree { 53 54 // Defines a mapping between each field's descriptor to the list of locations where 55 // its value(s) were was encountered. 56 private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField; 57 58 // Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for 59 // sub message location information. 60 Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField; 61 62 /** 63 * Construct a {@code TextFormatParseInfoTree}. 64 * 65 * @param locationsFromField a map of fields to location in the source code 66 * @param subtreeBuildersFromField a map of fields to parse tree location information builders 67 */ TextFormatParseInfoTree( Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField, Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField)68 private TextFormatParseInfoTree( 69 Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField, 70 Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) { 71 72 // The maps are unmodifiable. The values in the maps are unmodifiable. 73 Map<FieldDescriptor, List<TextFormatParseLocation>> locs = 74 new HashMap<FieldDescriptor, List<TextFormatParseLocation>>(); 75 for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) { 76 locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue())); 77 } 78 this.locationsFromField = Collections.unmodifiableMap(locs); 79 80 Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs = 81 new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>(); 82 for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) { 83 List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>(); 84 for (Builder subBuilder : kv.getValue()) { 85 submessagesOfField.add(subBuilder.build()); 86 } 87 subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField)); 88 } 89 this.subtreesFromField = Collections.unmodifiableMap(subs); 90 } 91 92 /** 93 * Retrieve all the locations of a field. 94 * 95 * @param fieldDescriptor the the @{link FieldDescriptor} of the desired field 96 * @return a list of the locations of values of the field. If there are not values 97 * or the field doesn't exist, an empty list is returned. 98 */ getLocations(final FieldDescriptor fieldDescriptor)99 public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) { 100 List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor); 101 return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result; 102 } 103 104 /** 105 * Get the location in the source of a field's value. 106 * 107 * <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed 108 * text. 109 * 110 * @param fieldDescriptor the @{link FieldDescriptor} of the desired field 111 * @param index the index of the value. 112 * @return the {@link TextFormatParseLocation} of the value 113 * @throws IllegalArgumentException index is out of range 114 */ getLocation(final FieldDescriptor fieldDescriptor, int index)115 public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) { 116 return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor); 117 } 118 119 /** 120 * Retrieve a list of all the location information trees for a sub message field. 121 * 122 * @param fieldDescriptor the @{link FieldDescriptor} of the desired field 123 * @return A list of {@link TextFormatParseInfoTree} 124 */ getNestedTrees(final FieldDescriptor fieldDescriptor)125 public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) { 126 List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor); 127 return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result; 128 } 129 130 /** 131 * Returns the parse info tree for the given field, which must be a message type. 132 * 133 * @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message 134 * @param index the index of message value. 135 * @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field 136 * doesn't exist or the index is out of range. 137 * @throws IllegalArgumentException if index is out of range 138 */ getNestedTree(final FieldDescriptor fieldDescriptor, int index)139 public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) { 140 return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor); 141 } 142 143 /** 144 * Create a builder for a {@code ParseInfoTree}. 145 * 146 * @return the builder 147 */ builder()148 public static Builder builder() { 149 return new Builder(); 150 } 151 getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor)152 private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) { 153 if (index >= list.size() || index < 0) { 154 throw new IllegalArgumentException(String.format("Illegal index field: %s, index %d", 155 fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index)); 156 } 157 return list.get(index); 158 } 159 160 /** 161 * Builder for a {@link TextFormatParseInfoTree}. 162 */ 163 public static class Builder { 164 165 private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField; 166 167 // Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for 168 // sub message location information. 169 private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField; 170 171 /** 172 * Create a root level {@ParseInfoTree} builder. 173 */ Builder()174 private Builder() { 175 locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>(); 176 subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>(); 177 } 178 179 /** 180 * Record the starting location of a single value for a field. 181 * 182 * @param fieldDescriptor the field 183 * @param location source code location information 184 */ setLocation( final FieldDescriptor fieldDescriptor, TextFormatParseLocation location)185 public Builder setLocation( 186 final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) { 187 List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor); 188 if (fieldLocations == null) { 189 fieldLocations = new ArrayList<TextFormatParseLocation>(); 190 locationsFromField.put(fieldDescriptor, fieldLocations); 191 } 192 fieldLocations.add(location); 193 return this; 194 } 195 196 /** 197 * Set for a sub message. 198 * 199 * <p>A new builder is created for a sub message. The builder that is returned is a new builder. 200 * The return is <em>not</em> the invoked {@code builder.getBuilderForSubMessageField}. 201 * 202 * @param fieldDescriptor the field whose value is the submessage 203 * @return a new Builder for the sub message 204 */ getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor)205 public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) { 206 List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor); 207 if (submessageBuilders == null) { 208 submessageBuilders = new ArrayList<Builder>(); 209 subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders); 210 } 211 Builder subtreeBuilder = new Builder(); 212 submessageBuilders.add(subtreeBuilder); 213 return subtreeBuilder; 214 } 215 216 /** 217 * Build the {@code TextFormatParseInfoTree}. 218 * 219 * @return the {@code TextFormatParseInfoTree} 220 */ build()221 public TextFormatParseInfoTree build() { 222 return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField); 223 } 224 } 225 } 226