forked from google/gdata-java-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExtensionDescription.java
More file actions
416 lines (351 loc) · 12.7 KB
/
Copy pathExtensionDescription.java
File metadata and controls
416 lines (351 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/* Copyright (c) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gdata.data;
import com.google.gdata.util.common.xml.XmlNamespace;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.util.common.xml.XmlWriter.Attribute;
import com.google.gdata.client.CoreErrorDomain;
import com.google.gdata.util.Namespaces;
import com.google.gdata.util.ParseException;
import org.xml.sax.Attributes;
import java.io.IOException;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* The ExtensionDescription class describes the attributes of an XML extension
* type. This description can be declared within an {@link ExtensionProfile}
* to indicate that the extension is expected within a particular
* {@link ExtensionPoint}.
*
*
*
*
* @see ExtensionProfile#declare(Class, ExtensionDescription)
*/
public class ExtensionDescription extends ExtensionPoint
implements Comparable<ExtensionDescription> {
/**
* The namespace of the XML extension type.
*/
private XmlNamespace namespace;
/**
* Local name of the XML extension type. A value of '*'
* indicates that all elements in the namespace will be handled
* by the Extension class.
*/
private String localName;
/**
* The Extension class used to parse and generate the extension type
*/
private Class<? extends Extension> extensionClass;
/**
* Specifies whether the extension is required within its parent extension
* point.
*/
private boolean required = false;
/**
* Specifies whether the extension type can be repeated within its parent
* extension point.
*/
private boolean repeatable = false;
/**
* Specifies whether the extension type aggregates the contents of multiple
* elements within its parent.
*/
private boolean aggregate = false;
/**
* Specifies whether the extension type allows arbitrary xml children.
*/
private boolean arbitraryXml = false;
/**
* If the extension type allows arbitrary xml, specifies whether the
* extension allows mixed content. If the extension type does not permit
* arbitrary xml, then mixed content is meaningless.
*/
private boolean mixedContent = false;
/**
* The Default interface defines a simple annotation model for describing
* the default {@link ExtensionDescription} of an {@link Extension} class. If
* this annotation is place on an @{link Extension} class, the
* {@link ExtensionDescription#getDefaultDescription(Class)} method can be
* used to retrieve default description for the class.
*
* @see ExtensionDescription#getDefaultDescription(Class)
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Default {
/**
* The default namespace alias associated with this extension.
*/
public String nsAlias();
/**
* The default namespace uri associated with this extension.
*/
public String nsUri();
/**
* The default XML element local name associated with this extension.
*/
public String localName();
/**
* {@code true} if the extension is required by default, {@code false}
* otherwise.
*/
public boolean isRequired() default false;
/**
* {@code true} if the extension is repeatable by default, {@code false}
* otherwise.
*/
public boolean isRepeatable() default false;
/**
* {@code true} if the extension is aggregate by default, {@code false}
* otherwise.
*/
public boolean isAggregate() default false;
/**
* {@code true} if the extension allows arbitrary XML, {@code false}
* otherwise.
*/
public boolean allowsArbitraryXml() default false;
/**
* {@code true} if the extension allows mixed content, {@code false}
* otherwise.
*/
public boolean allowsMixedContent() default false;
}
/**
* Returns the default {@link ExtensionDescription} for the specified
* Extension class.
*
* @param extensionClass the target extension class.
* @return default description for the target extension class.
*
* @throws IllegalArgumentException if a default description could not be
* fourn for the extension class.
*/
public static ExtensionDescription getDefaultDescription(
Class<? extends Extension> extensionClass) {
Default defAnnot = extensionClass.getAnnotation(Default.class);
if (defAnnot == null) {
throw new IllegalArgumentException("No default description found for "
+ extensionClass);
}
return new ExtensionDescription(
extensionClass,
new XmlNamespace(defAnnot.nsAlias(), defAnnot.nsUri()),
defAnnot.localName(),
defAnnot.isRequired(),
defAnnot.isRepeatable(),
defAnnot.isAggregate(),
defAnnot.allowsArbitraryXml(),
defAnnot.allowsMixedContent());
}
/**
* Constructs an uninitialized ExtensionDescription.
*/
public ExtensionDescription() {}
/**
* Constructs a new ExtensionDescription populated with the parameter
* values.
*/
public ExtensionDescription(Class<? extends Extension> extensionClass,
XmlNamespace namespace,
String localName,
boolean required,
boolean repeatable,
boolean aggregate) {
this(extensionClass, namespace, localName, required, repeatable, aggregate, false, false);
}
/**
* Constructs a new ExtensionDescription populated with the parameter
* values.
*/
public ExtensionDescription(Class<? extends Extension> extensionClass,
XmlNamespace namespace,
String localName,
boolean required,
boolean repeatable,
boolean aggregate,
boolean arbitraryXml,
boolean mixedContent) {
this.namespace = namespace;
this.localName = localName;
this.extensionClass = extensionClass;
this.required = required;
this.repeatable = repeatable;
this.aggregate = aggregate;
this.arbitraryXml = arbitraryXml;
this.mixedContent = mixedContent;
}
/**
* Constructs a new ExtensionDescription for an optional, non-repeating
* simple element.
*/
public ExtensionDescription(Class<? extends Extension> extensionClass,
XmlNamespace namespace,
String localName) {
this(extensionClass, namespace, localName, false, false, false);
}
public void setNamespace(XmlNamespace namespace) {
this.namespace = namespace;
}
final public XmlNamespace getNamespace() { return namespace; }
public void setLocalName(String localName) {
this.localName = localName;
}
final public String getLocalName() { return localName; }
public void setExtensionClass(Class<? extends Extension> extensionClass) {
this.extensionClass = extensionClass;
}
final public Class<? extends Extension> getExtensionClass() {
return extensionClass;
}
public void setRequired(boolean required) {
this.required = required;
}
final public boolean isRequired() { return required; }
public void setRepeatable(boolean repeatable) {
this.repeatable = repeatable;
}
final public boolean isRepeatable() { return repeatable; }
public void setAggregate(boolean aggregate) {
this.aggregate = aggregate;
}
final public boolean isAggregate() { return aggregate; }
public void setArbitraryXml(boolean arbitraryXml) {
this.arbitraryXml = arbitraryXml;
}
final public boolean allowsArbitraryXml() { return arbitraryXml; }
public void setMixedContent(boolean mixedContent) {
this.mixedContent = mixedContent;
}
final public boolean allowsMixedContent() { return mixedContent; }
/**
* Defines a natural ordering for ExtensionDescription based upon
* the qualified name of the mapped XML element. Elements with no
* namespace are considered to precede all others.
*/
public int compareTo(ExtensionDescription desc) {
String ns1 = namespace.getUri();
if (ns1 == null) {
ns1 = "";
}
String ns2 = desc.namespace.getUri();
if (ns2 == null) {
ns2 = "";
}
int nscomp = ns1.compareTo(ns2);
if (nscomp != 0) {
return nscomp;
}
return localName.compareTo(desc.localName);
}
/** Namespace of the corresponding XML element. */
/**
* Reads the ExtensionDescription XML format
*/
public class Handler extends ExtensionPoint.ExtensionHandler {
public Handler(ExtensionProfile configProfile, ClassLoader configLoader,
List<XmlNamespace> namespaces, Attributes attrs)
throws ParseException {
super(configProfile, ExtensionDescription.class);
String nsValue = attrs.getValue("", "namespace");
if (nsValue == null) {
throw new ParseException(
CoreErrorDomain.ERR.missingNamespace);
}
// Find the namespace in the list of declared NamespaceDescriptions.
// The attribute value can match either the alias or the uri
for (XmlNamespace declaredNs : namespaces) {
if (declaredNs.getAlias().equals(nsValue) ||
declaredNs.getUri().equals(nsValue)) {
namespace = declaredNs;
break;
}
}
if (namespace == null) {
ParseException pe = new ParseException(
CoreErrorDomain.ERR.missingNamespaceDescription);
pe.setInternalReason("No matching NamespaceDescription for " +
nsValue);
throw pe;
}
localName = attrs.getValue("", "localName");
if (localName == null) {
throw new ParseException(
CoreErrorDomain.ERR.missingLocalName);
}
String extensionClassName = attrs.getValue("", "extensionClass");
if (extensionClassName == null) {
throw new ParseException(
CoreErrorDomain.ERR.missingExtensionClass);
}
try {
Class<?> extClass = configLoader.loadClass(extensionClassName);
if (!Extension.class.isAssignableFrom(extClass)) {
throw new ParseException(
CoreErrorDomain.ERR.mustImplementExtension);
}
extensionClass = (Class<? extends Extension>) extClass;
} catch (ClassNotFoundException e) {
ParseException pe = new ParseException(
CoreErrorDomain.ERR.cantLoadExtensionClass, e);
pe.setInternalReason("Unable to load extensionClass: " +
extensionClassName);
throw pe;
}
Boolean bool = getBooleanAttribute(attrs, "required");
required = (bool != null) && bool.booleanValue();
bool = getBooleanAttribute(attrs, "repeatable");
repeatable = (bool != null) && bool.booleanValue();
bool = getBooleanAttribute(attrs, "aggregate");
aggregate = (bool != null) && bool.booleanValue();
bool = getBooleanAttribute(attrs, "arbitraryXml");
arbitraryXml = (bool != null) && bool.booleanValue();
bool = getBooleanAttribute(attrs, "mixedContent");
mixedContent = (bool != null) && bool.booleanValue();
}
}
/**
* Generates XML in the external config format.
*
* @param w
* Output writer.
*
* @param extProfile
* Extension profile.
*
* @throws IOException
*/
public void generateConfig(XmlWriter w,
ExtensionProfile extProfile) throws IOException {
List<Attribute> attrs = new ArrayList<Attribute>();
attrs.add(new Attribute("namespace", namespace.getUri()));
attrs.add(new Attribute("localName", localName));
attrs.add(new Attribute("extensionClass", extensionClass.getName()));
attrs.add(new Attribute("required", required));
attrs.add(new Attribute("repeatable", repeatable));
attrs.add(new Attribute("aggregate", aggregate));
attrs.add(new Attribute("arbitraryXml", arbitraryXml));
attrs.add(new Attribute("mixedContent", mixedContent));
generateStartElement(w, Namespaces.gdataConfigNs, "extensionDescription",
attrs, null);
generateExtensions(w, extProfile);
w.endElement(Namespaces.gdataConfigNs, "extensionDescription");
}
}