• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {@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 <emph>not</emph> 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