3434
3535import javax .annotation .Nullable ;
3636
37+ import com .google .common .base .Preconditions ;
3738import com .google .common .collect .ImmutableMap ;
3839import com .google .common .collect .ImmutableSet ;
3940import com .google .common .collect .Maps ;
4344@ TCB
4445public final class CssSchema {
4546
46- static final class Property {
47+ /**
48+ * Describes how CSS interprets tokens after the ":" for a property.
49+ * For example, if the property name "color" maps to this, then it
50+ * should record that '#' literals are innocuous colors, and that functions
51+ * like "rgb", "rgba", "hsl", etc. are allowed functions.
52+ */
53+ public static final class Property {
4754 /** A bitfield of BIT_* constants describing groups of allowed tokens. */
4855 final int bits ;
4956 /** Specific allowed values. */
@@ -53,13 +60,60 @@ static final class Property {
5360 */
5461 final ImmutableMap <String , String > fnKeys ;
5562
56- Property (
63+ /**
64+ * @param bits A bitfield of BIT_* constants describing groups of allowed tokens.
65+ * @param literals Specific allowed values.
66+ * @param fnKeys Maps lower-case function tokens to the schema key for their parameters.
67+ */
68+ public Property (
5769 int bits , ImmutableSet <String > literals ,
5870 ImmutableMap <String , String > fnKeys ) {
5971 this .bits = bits ;
6072 this .literals = literals ;
6173 this .fnKeys = fnKeys ;
6274 }
75+
76+ @ Override
77+ public int hashCode () {
78+ final int prime = 31 ;
79+ int result = 1 ;
80+ result = prime * result + bits ;
81+ result = prime * result + ((fnKeys == null ) ? 0 : fnKeys .hashCode ());
82+ result = prime * result + ((literals == null ) ? 0 : literals .hashCode ());
83+ return result ;
84+ }
85+
86+ @ Override
87+ public boolean equals (Object obj ) {
88+ if (this == obj ) {
89+ return true ;
90+ }
91+ if (obj == null ) {
92+ return false ;
93+ }
94+ if (getClass () != obj .getClass ()) {
95+ return false ;
96+ }
97+ Property other = (Property ) obj ;
98+ if (bits != other .bits ) {
99+ return false ;
100+ }
101+ if (fnKeys == null ) {
102+ if (other .fnKeys != null ) {
103+ return false ;
104+ }
105+ } else if (!fnKeys .equals (other .fnKeys )) {
106+ return false ;
107+ }
108+ if (literals == null ) {
109+ if (other .literals != null ) {
110+ return false ;
111+ }
112+ } else if (!literals .equals (other .literals )) {
113+ return false ;
114+ }
115+ return true ;
116+ }
63117 }
64118
65119 static final int BIT_QUANTITY = 1 ;
@@ -100,17 +154,52 @@ public static CssSchema withProperties(
100154 return new CssSchema (propertiesBuilder .build ());
101155 }
102156
157+ /**
158+ * A schema that includes all and only the named properties.
159+ *
160+ * @param properties maps lower-case CSS property names to property objects.
161+ */
162+ public static CssSchema withProperties (
163+ Map <? extends String , ? extends Property > properties ) {
164+ ImmutableMap <String , Property > propertyMap =
165+ ImmutableMap .copyOf (properties );
166+ // check that all fnKeys are defined in properties.
167+ for (Map .Entry <String , Property > e : propertyMap .entrySet ()) {
168+ Property property = e .getValue ();
169+ for (String fnKey : property .fnKeys .values ()) {
170+ if (!propertyMap .containsKey (fnKey )) {
171+ throw new IllegalArgumentException (
172+ "Property map is not self contained. " + e .getValue ()
173+ + " depends on undefined function key " + fnKey );
174+ }
175+ }
176+ }
177+ return new CssSchema (propertyMap );
178+ }
179+
103180 /**
104181 * A schema that represents the union of the input schemas.
105182 *
106183 * @return A schema that allows all and only CSS properties that are allowed
107184 * by at least one of the inputs.
185+ * @throws IllegalArgumentException if two schemas have properties with the
186+ * same name, but different (per .equals) {@link Property} values.
108187 */
109188 public static CssSchema union (CssSchema ... cssSchemas ) {
110189 if (cssSchemas .length == 1 ) { return cssSchemas [0 ]; }
111190 Map <String , Property > properties = Maps .newLinkedHashMap ();
112191 for (CssSchema cssSchema : cssSchemas ) {
113- properties .putAll (cssSchema .properties );
192+ for (Map .Entry <String , Property > e : cssSchema .properties .entrySet ()) {
193+ String name = e .getKey ();
194+ Property newProp = e .getValue ();
195+ Preconditions .checkNotNull (name );
196+ Preconditions .checkNotNull (newProp );
197+ Property oldProp = properties .put (name , newProp );
198+ if (oldProp != null && !oldProp .equals (newProp )) {
199+ throw new IllegalArgumentException (
200+ "Duplicate irreconcilable definitions for " + name );
201+ }
202+ }
114203 }
115204 return new CssSchema (ImmutableMap .copyOf (properties ));
116205 }
0 commit comments