• 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 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 
41 /**
42  * Data structure which is populated with the locations of each field value parsed from the text.
43  *
44  * <p>The locations of primary fields values are retrieved by {@code getLocation} or {@code
45  * getLocations}. The locations of sub message values are within nested {@code
46  * TextFormatParseInfoTree}s and are retrieve by {@code getNestedTree} or {@code getNestedTrees}.
47  *
48  * <p>The {@code TextFormatParseInfoTree} is created by a Builder.
49  */
50 public class TextFormatParseInfoTree {
51 
52   // Defines a mapping between each field's descriptor to the list of locations where
53   // its value(s) were was encountered.
54   private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
55 
56   // Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for
57   // sub message location information.
58   Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField;
59 
60   /**
61    * Construct a {@code TextFormatParseInfoTree}.
62    *
63    * @param locationsFromField a map of fields to location in the source code
64    * @param subtreeBuildersFromField a map of fields to parse tree location information builders
65    */
TextFormatParseInfoTree( Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField, Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField)66   private TextFormatParseInfoTree(
67       Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField,
68       Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) {
69 
70     // The maps are unmodifiable.  The values in the maps are unmodifiable.
71     Map<FieldDescriptor, List<TextFormatParseLocation>> locs =
72         new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
73     for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) {
74       locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue()));
75     }
76     this.locationsFromField = Collections.unmodifiableMap(locs);
77 
78     Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs =
79         new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>();
80     for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) {
81       List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>();
82       for (Builder subBuilder : kv.getValue()) {
83         submessagesOfField.add(subBuilder.build());
84       }
85       subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField));
86     }
87     this.subtreesFromField = Collections.unmodifiableMap(subs);
88   }
89 
90   /**
91    * Retrieve all the locations of a field.
92    *
93    * @param fieldDescriptor the @{link FieldDescriptor} of the desired field
94    * @return a list of the locations of values of the field. If there are not values or the field
95    *     doesn't exist, an empty list is returned.
96    */
getLocations(final FieldDescriptor fieldDescriptor)97   public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) {
98     List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor);
99     return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result;
100   }
101 
102   /**
103    * Get the location in the source of a field's value.
104    *
105    * <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed
106    * text.
107    *
108    * @param fieldDescriptor the @{link FieldDescriptor} of the desired field
109    * @param index the index of the value.
110    * @return the {@link TextFormatParseLocation} of the value
111    * @throws IllegalArgumentException index is out of range
112    */
getLocation(final FieldDescriptor fieldDescriptor, int index)113   public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) {
114     return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor);
115   }
116 
117   /**
118    * Retrieve a list of all the location information trees for a sub message field.
119    *
120    * @param fieldDescriptor the @{link FieldDescriptor} of the desired field
121    * @return A list of {@link TextFormatParseInfoTree}
122    */
getNestedTrees(final FieldDescriptor fieldDescriptor)123   public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) {
124     List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor);
125     return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result;
126   }
127 
128   /**
129    * Returns the parse info tree for the given field, which must be a message type.
130    *
131    * @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message
132    * @param index the index of message value.
133    * @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field
134    *     doesn't exist or the index is out of range.
135    * @throws IllegalArgumentException if index is out of range
136    */
getNestedTree(final FieldDescriptor fieldDescriptor, int index)137   public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) {
138     return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor);
139   }
140 
141   /**
142    * Create a builder for a {@code ParseInfoTree}.
143    *
144    * @return the builder
145    */
builder()146   public static Builder builder() {
147     return new Builder();
148   }
149 
getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor)150   private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) {
151     if (index >= list.size() || index < 0) {
152       throw new IllegalArgumentException(
153           String.format(
154               "Illegal index field: %s, index %d",
155               fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index));
156     }
157     return list.get(index);
158   }
159 
160   /** Builder for a {@link TextFormatParseInfoTree}. */
161   public static class Builder {
162 
163     private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
164 
165     // Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for
166     // sub message location information.
167     private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField;
168 
169     /** Create a root level {@ParseInfoTree} builder. */
Builder()170     private Builder() {
171       locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
172       subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>();
173     }
174 
175     /**
176      * Record the starting location of a single value for a field.
177      *
178      * @param fieldDescriptor the field
179      * @param location source code location information
180      */
setLocation( final FieldDescriptor fieldDescriptor, TextFormatParseLocation location)181     public Builder setLocation(
182         final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) {
183       List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor);
184       if (fieldLocations == null) {
185         fieldLocations = new ArrayList<TextFormatParseLocation>();
186         locationsFromField.put(fieldDescriptor, fieldLocations);
187       }
188       fieldLocations.add(location);
189       return this;
190     }
191 
192     /**
193      * Set for a sub message.
194      *
195      * <p>A new builder is created for a sub message. The builder that is returned is a new builder.
196      * The return is <em>not</em> the invoked {@code builder.getBuilderForSubMessageField}.
197      *
198      * @param fieldDescriptor the field whose value is the submessage
199      * @return a new Builder for the sub message
200      */
getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor)201     public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) {
202       List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor);
203       if (submessageBuilders == null) {
204         submessageBuilders = new ArrayList<Builder>();
205         subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders);
206       }
207       Builder subtreeBuilder = new Builder();
208       submessageBuilders.add(subtreeBuilder);
209       return subtreeBuilder;
210     }
211 
212     /**
213      * Build the {@code TextFormatParseInfoTree}.
214      *
215      * @return the {@code TextFormatParseInfoTree}
216      */
build()217     public TextFormatParseInfoTree build() {
218       return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField);
219     }
220   }
221 }
222