• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import com.google.protobuf.Descriptors.FieldDescriptor;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 
18 /**
19  * Data structure which is populated with the locations of each field value parsed from the text.
20  *
21  * <p>The locations of primary fields values are retrieved by {@code getLocation} or {@code
22  * getLocations}. The locations of sub message values are within nested {@code
23  * TextFormatParseInfoTree}s and are retrieve by {@code getNestedTree} or {@code getNestedTrees}.
24  *
25  * <p>The {@code TextFormatParseInfoTree} is created by a Builder.
26  */
27 public class TextFormatParseInfoTree {
28 
29   // Defines a mapping between each field's descriptor to the list of locations where
30   // its value(s) were was encountered.
31   private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
32 
33   // Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for
34   // sub message location information.
35   Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField;
36 
37   /**
38    * Construct a {@code TextFormatParseInfoTree}.
39    *
40    * @param locationsFromField a map of fields to location in the source code
41    * @param subtreeBuildersFromField a map of fields to parse tree location information builders
42    */
TextFormatParseInfoTree( Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField, Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField)43   private TextFormatParseInfoTree(
44       Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField,
45       Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) {
46 
47     // The maps are unmodifiable.  The values in the maps are unmodifiable.
48     Map<FieldDescriptor, List<TextFormatParseLocation>> locs =
49         new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
50     for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) {
51       locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue()));
52     }
53     this.locationsFromField = Collections.unmodifiableMap(locs);
54 
55     Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs =
56         new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>();
57     for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) {
58       List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>();
59       for (Builder subBuilder : kv.getValue()) {
60         submessagesOfField.add(subBuilder.build());
61       }
62       subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField));
63     }
64     this.subtreesFromField = Collections.unmodifiableMap(subs);
65   }
66 
67   /**
68    * Retrieve all the locations of a field.
69    *
70    * @param fieldDescriptor the {@link FieldDescriptor} of the desired field
71    * @return a list of the locations of values of the field. If there are not values or the field
72    *     doesn't exist, an empty list is returned.
73    */
getLocations(final FieldDescriptor fieldDescriptor)74   public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) {
75     List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor);
76     return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result;
77   }
78 
79   /**
80    * Get the location in the source of a field's value.
81    *
82    * <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed
83    * text.
84    *
85    * @param fieldDescriptor the {@link FieldDescriptor} of the desired field
86    * @param index the index of the value.
87    * @return the {@link TextFormatParseLocation} of the value
88    * @throws IllegalArgumentException index is out of range
89    */
getLocation(final FieldDescriptor fieldDescriptor, int index)90   public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) {
91     return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor);
92   }
93 
94   /**
95    * Retrieve a list of all the location information trees for a sub message field.
96    *
97    * @param fieldDescriptor the {@link FieldDescriptor} of the desired field
98    * @return A list of {@link TextFormatParseInfoTree}
99    */
getNestedTrees(final FieldDescriptor fieldDescriptor)100   public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) {
101     List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor);
102     return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result;
103   }
104 
105   /**
106    * Returns the parse info tree for the given field, which must be a message type.
107    *
108    * @param fieldDescriptor the {@link FieldDescriptor} of the desired sub message
109    * @param index the index of message value.
110    * @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field
111    *     doesn't exist or the index is out of range.
112    * @throws IllegalArgumentException if index is out of range
113    */
getNestedTree(final FieldDescriptor fieldDescriptor, int index)114   public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) {
115     return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor);
116   }
117 
118   /**
119    * Create a builder for a {@code ParseInfoTree}.
120    *
121    * @return the builder
122    */
builder()123   public static Builder builder() {
124     return new Builder();
125   }
126 
getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor)127   private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) {
128     if (index >= list.size() || index < 0) {
129       throw new IllegalArgumentException(
130           String.format(
131               "Illegal index field: %s, index %d",
132               fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index));
133     }
134     return list.get(index);
135   }
136 
137   /** Builder for a {@link TextFormatParseInfoTree}. */
138   public static class Builder {
139 
140     private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
141 
142     // Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for
143     // sub message location information.
144     private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField;
145 
146     /** Create a root level {@ParseInfoTree} builder. */
Builder()147     private Builder() {
148       locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
149       subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>();
150     }
151 
152     /**
153      * Record the starting location of a single value for a field.
154      *
155      * @param fieldDescriptor the field
156      * @param location source code location information
157      */
setLocation( final FieldDescriptor fieldDescriptor, TextFormatParseLocation location)158     public Builder setLocation(
159         final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) {
160       List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor);
161       if (fieldLocations == null) {
162         fieldLocations = new ArrayList<TextFormatParseLocation>();
163         locationsFromField.put(fieldDescriptor, fieldLocations);
164       }
165       fieldLocations.add(location);
166       return this;
167     }
168 
169     /**
170      * Set for a sub message.
171      *
172      * <p>A new builder is created for a sub message. The builder that is returned is a new builder.
173      * The return is <em>not</em> the invoked {@code builder.getBuilderForSubMessageField}.
174      *
175      * @param fieldDescriptor the field whose value is the submessage
176      * @return a new Builder for the sub message
177      */
getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor)178     public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) {
179       List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor);
180       if (submessageBuilders == null) {
181         submessageBuilders = new ArrayList<Builder>();
182         subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders);
183       }
184       Builder subtreeBuilder = new Builder();
185       submessageBuilders.add(subtreeBuilder);
186       return subtreeBuilder;
187     }
188 
189     /**
190      * Build the {@code TextFormatParseInfoTree}.
191      *
192      * @return the {@code TextFormatParseInfoTree}
193      */
build()194     public TextFormatParseInfoTree build() {
195       return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField);
196     }
197   }
198 }
199