Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / third_party / cacheinvalidation / src / java / com / google / ipc / invalidation / common / ProtoValidator.java
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.ipc.invalidation.common;
17
18 import com.google.common.base.Preconditions;
19 import com.google.ipc.invalidation.common.ProtoValidator.FieldInfo.Presence;
20 import com.google.ipc.invalidation.util.BaseLogger;
21 import com.google.ipc.invalidation.util.TypedUtil;
22 import com.google.protobuf.MessageLite;
23
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.NoSuchElementException;
29 import java.util.Set;
30
31
32 /**
33  * Base class for writing protocol buffer validators. This is used in conjunction with
34  * {@code ProtoAccessorGenerator}.
35  *
36  */
37 public abstract class ProtoValidator {
38   /** Class providing access to protocol buffer fields in a generic way. */
39   public interface Accessor {
40     public boolean hasField(MessageLite message, Descriptor field);
41     public Object getField(MessageLite message, Descriptor field);
42     public Collection<String> getAllFieldNames();
43   }
44
45   /** Class naming a protocol buffer field in a generic way. */
46   public static class Descriptor {
47     private final String name;
48
49     public Descriptor(String name) {
50       this.name = name;
51     }
52     /** Returns the name of the described field. */
53     public String getName() {
54       return name;
55     }
56     @Override
57     public String toString() {
58       return "Descriptor for field " + name;
59     }
60   }
61
62   /** Describes how to validate a message. */
63   public static class MessageInfo {
64     /** Protocol buffer descriptor for the message. */
65     private final Accessor messageAccessor;
66
67     /** Information about required and optional fields in this message. */
68     private final Set<FieldInfo> fieldInfo = new HashSet<FieldInfo>();
69
70     private int numRequiredFields;
71
72     /**
73      * Constructs a message info.
74      *
75      * @param messageAccessor descriptor for the protocol buffer
76      * @param fields information about the fields
77      */
78     public MessageInfo(Accessor messageAccessor, FieldInfo... fields) {
79       // Track which fields in the message descriptor have not yet been covered by a FieldInfo.
80       // We'll use this to verify that we get a FieldInfo for every field.
81       Set<String> unusedDescriptors = new HashSet<String>();
82       unusedDescriptors.addAll(messageAccessor.getAllFieldNames());
83
84       this.messageAccessor = messageAccessor;
85       for (FieldInfo info : fields) {
86         // Lookup the field given the name in the FieldInfo.
87         boolean removed = TypedUtil.remove(unusedDescriptors, info.getFieldDescriptor().getName());
88         Preconditions.checkState(removed, "Bad field: %s", info.getFieldDescriptor().getName());
89
90         // Add the field info to the number -> info map.
91         fieldInfo.add(info);
92
93         if (info.getPresence() == Presence.REQUIRED) {
94           ++numRequiredFields;
95         }
96       }
97       Preconditions.checkState(unusedDescriptors.isEmpty(), "Not all fields specified in %s: %s",
98           messageAccessor, unusedDescriptors);
99     }
100
101     /** Returns the stored field information. */
102     protected Collection<FieldInfo> getAllFields() {
103       return fieldInfo;
104     }
105
106     /**
107      * Function called after the presence/absence of all fields in this message and its child
108      * messages have been verified. Should be overridden to enforce additional semantic constraints
109      * beyond field presence/absence if needed.
110      */
111     protected boolean postValidate(MessageLite message) {
112       return true;
113     }
114
115     /** Returns the number of required fields for messages of this type. */
116     public int getNumRequiredFields() {
117       return numRequiredFields;
118     }
119   }
120
121   /** Describes a field in a message. */
122   protected static class FieldInfo {
123     /**
124      * Whether the field is required or optional. A repeated field where at least one value
125      * must be set should use {@code REQUIRED}.
126      */
127     enum Presence {
128       REQUIRED,
129       OPTIONAL
130     }
131
132     /** Name of the field in the containing message. */
133     private final Descriptor fieldDescriptor;
134
135     /** Whether the field is required or optional. */
136     private final Presence presence;
137
138     /** If not {@code null}, message info describing how to validate the field. */
139     private final MessageInfo messageInfo;
140
141     /**
142      * Constructs an instance.
143      *
144      * @param fieldDescriptor identifier for the field
145      * @param presence required/optional
146      * @param messageInfo if not {@code null}, describes how to validate the field
147      */
148     FieldInfo(Descriptor fieldDescriptor, Presence presence,
149         MessageInfo messageInfo) {
150       this.fieldDescriptor = fieldDescriptor;
151       this.presence = presence;
152       this.messageInfo = messageInfo;
153     }
154
155     /** Returns the name of the field. */
156     public Descriptor getFieldDescriptor() {
157       return fieldDescriptor;
158     }
159
160     /** Returns the presence information for the field. */
161     Presence getPresence() {
162       return presence;
163     }
164
165     /** Returns the validation information for the field. */
166     MessageInfo getMessageInfo() {
167       return messageInfo;
168     }
169
170     /** Returns whether the field needs additional validation. */
171     boolean requiresAdditionalValidation() {
172       return messageInfo != null;
173     }
174
175     /**
176      * Returns a new instance describing a required field with name {@code fieldName} and validation
177      * specified by {@code messageInfo}.
178      */
179     public static FieldInfo newRequired(Descriptor fieldDescriptor, MessageInfo messageInfo) {
180       return new FieldInfo(fieldDescriptor, Presence.REQUIRED,
181           Preconditions.checkNotNull(messageInfo, "messageInfo cannot be null"));
182     }
183
184     /**
185      * Returns a new instance describing a required field with name {@code fieldName} and no
186      * additional validation.
187      */
188     public static FieldInfo newRequired(Descriptor fieldDescriptor) {
189       return new FieldInfo(fieldDescriptor, Presence.REQUIRED, null);
190     }
191
192     /**
193      * Returns a new instance describing an optional field with name {@code fieldName} and
194      * validation specified by {@code messageInfo}.
195      */
196     public static FieldInfo newOptional(Descriptor fieldDescriptor, MessageInfo messageInfo) {
197       return new FieldInfo(fieldDescriptor, Presence.OPTIONAL,
198           Preconditions.checkNotNull(messageInfo));
199     }
200
201     /**
202      * Returns a new instance describing an optional field with name {@code fieldName} and no
203      * additional validation.
204      */
205     public static FieldInfo newOptional(Descriptor fieldDescriptor) {
206       return new FieldInfo(fieldDescriptor, Presence.OPTIONAL, null);
207     }
208   }
209
210   /** Logger for errors */
211   protected final BaseLogger logger;
212
213   protected ProtoValidator(BaseLogger logger) {
214     this.logger = logger;
215   }
216
217   /**
218    * Returns an {@link Iterable} over the instance(s) of {@code field} in {@code message}. This
219    * provides a uniform way to handle both singleton and repeated fields in protocol buffers, which
220    * are accessed using different calls in the protocol buffer API.
221    */
222   @SuppressWarnings("unchecked")
223   
224   protected static <FieldType> Iterable<FieldType> getFieldIterable(final MessageLite message,
225       final Accessor messageAccessor, final Descriptor fieldDescriptor) {
226     final Object obj = messageAccessor.getField(message, fieldDescriptor);
227     if (obj instanceof List) {
228       return (List<FieldType>) obj;
229     } else {
230       // Otherwise, just use a singleton iterator.
231       return new Iterable<FieldType>() {
232         @Override
233         public Iterator<FieldType> iterator() {
234           return new Iterator<FieldType>() {
235             boolean done;
236             @Override
237             public boolean hasNext() {
238               return !done;
239             }
240
241             @Override
242             public FieldType next() {
243               if (done) {
244                 throw new NoSuchElementException();
245               }
246               done = true;
247               return (FieldType) obj;
248             }
249
250             @Override
251             public void remove() {
252               throw new UnsupportedOperationException("Not allowed");
253             }
254           };
255         }
256       };
257     }
258   }
259
260   /**
261    * Returns whether {@code message} is valid.
262    * @param messageInfo specification of validity for {@code message}
263    */
264   
265   protected boolean checkMessage(MessageLite message, MessageInfo messageInfo) {
266     for (FieldInfo fieldInfo : messageInfo.getAllFields()) {
267       Descriptor fieldDescriptor = fieldInfo.getFieldDescriptor();
268       boolean isFieldPresent =
269           messageInfo.messageAccessor.hasField(message, fieldDescriptor);
270
271       // If the field must be present but isn't, fail.
272       if ((fieldInfo.getPresence() == FieldInfo.Presence.REQUIRED) && !(isFieldPresent)) {
273         logger.warning("Required field not set: %s", fieldInfo.getFieldDescriptor().getName());
274         return false;
275       }
276
277       // If the field is present and requires its own validation, validate it.
278       if (isFieldPresent && fieldInfo.requiresAdditionalValidation()) {
279         for (MessageLite subMessage : TiclMessageValidator2.<MessageLite>getFieldIterable(
280             message, messageInfo.messageAccessor, fieldDescriptor)) {
281           if (!checkMessage(subMessage, fieldInfo.getMessageInfo())) {
282             return false;
283           }
284         }
285       }
286     }
287
288     // Once we've validated all fields, post-validate this message.
289     if (!messageInfo.postValidate(message)) {
290       logger.info("Failed post-validation of message (%s): %s",
291           message.getClass().getSimpleName(), message);
292       return false;
293     }
294     return true;
295   }
296 }