1 package com.atlassian.plugin.schema.spi;
2
3 import com.atlassian.plugin.Permissions;
4 import com.atlassian.plugin.util.resource.AlternativeResourceLoader;
5 import com.atlassian.security.xml.SecureXmlParserFactory;
6 import com.google.common.collect.ImmutableSet;
7 import org.dom4j.Document;
8 import org.dom4j.DocumentException;
9 import org.dom4j.io.SAXReader;
10
11 import java.net.URL;
12
13 import static com.google.common.base.Preconditions.checkNotNull;
14 import static java.util.Collections.emptySet;
15
16
17
18
19 public final class DocumentBasedSchema implements Schema {
20 private final String elementName;
21
22 private final String name;
23 private final String description;
24
25 private final String path;
26 private final String fileName;
27 private final String complexType;
28 private final String maxOccurs;
29 private final Iterable<String> requiredPermissions;
30 private final Iterable<String> optionalPermissions;
31 private final AlternativeResourceLoader resourceLoader;
32 private final SchemaTransformer schemaTransformer;
33
34 private DocumentBasedSchema(String elementName,
35 String name,
36 String description,
37 String path,
38 String fileName,
39 String complexType,
40 String maxOccurs,
41 Iterable<String> requiredPermissions,
42 Iterable<String> optionalPermissions,
43 AlternativeResourceLoader resourceLoader,
44 SchemaTransformer schemaTransformer) {
45 this.elementName = elementName;
46
47 this.name = name == null ? IdUtils.dashesToTitle(elementName) : name;
48 this.description = description == null ? "A " + name + " module" : description;
49
50 this.path = path;
51 this.fileName = fileName;
52 this.complexType = complexType;
53 this.maxOccurs = maxOccurs;
54 this.requiredPermissions = requiredPermissions;
55 this.optionalPermissions = optionalPermissions;
56 this.resourceLoader = resourceLoader;
57 this.schemaTransformer = schemaTransformer;
58 }
59
60 @Override
61 public String getName() {
62 return name;
63 }
64
65 @Override
66 public String getDescription() {
67 return description;
68 }
69
70 @Override
71 public String getFileName() {
72 return fileName;
73 }
74
75 @Override
76 public String getElementName() {
77 return elementName;
78 }
79
80 @Override
81 public String getComplexType() {
82 return complexType;
83 }
84
85 @Override
86 public String getMaxOccurs() {
87 return maxOccurs;
88 }
89
90 @Override
91 public Iterable<String> getRequiredPermissions() {
92 return requiredPermissions;
93 }
94
95 @Override
96 public Iterable<String> getOptionalPermissions() {
97 return optionalPermissions;
98 }
99
100 @Override
101 public Document getDocument() {
102 return getDocument(resourceLoader, path, schemaTransformer);
103 }
104
105 public static DynamicSchemaBuilder builder() {
106 return new DynamicSchemaBuilder();
107 }
108
109 public static DynamicSchemaBuilder builder(String id) {
110 return new DynamicSchemaBuilder(id);
111 }
112
113 public static class DynamicSchemaBuilder {
114 private String name;
115 private String description;
116 private String path;
117 private String fileName;
118 private String elementName;
119 private String complexType;
120 private String maxOccurs = "unbounded";
121
122
123 private Iterable<String> requiredPermissions = ImmutableSet.of(Permissions.EXECUTE_JAVA, Permissions.GENERATE_ANY_HTML);
124
125 private Iterable<String> optionalPermissions = emptySet();
126 private AlternativeResourceLoader resourceLoader;
127 private SchemaTransformer schemaTransformer = SchemaTransformer.IDENTITY;
128
129 public DynamicSchemaBuilder() {
130 }
131
132 public DynamicSchemaBuilder(String elementName) {
133 this.elementName = elementName;
134 this.fileName = elementName + ".xsd";
135 this.path = "/xsd/" + this.fileName;
136 this.complexType = IdUtils.dashesToCamelCase(elementName) + "Type";
137 }
138
139 public DynamicSchemaBuilder setName(String name) {
140 this.name = name;
141 return this;
142 }
143
144 public DynamicSchemaBuilder setDescription(String description) {
145 this.description = description;
146 return this;
147 }
148
149 public DynamicSchemaBuilder setPath(String path) {
150 this.path = path;
151 return this;
152 }
153
154 public DynamicSchemaBuilder setFileName(String fileName) {
155 this.fileName = fileName;
156 return this;
157 }
158
159 public DynamicSchemaBuilder setElementName(String elementName) {
160 this.elementName = elementName;
161 return this;
162 }
163
164 public DynamicSchemaBuilder setRequiredPermissions(Iterable<String> permissions) {
165 this.requiredPermissions = permissions;
166 return this;
167 }
168
169 public DynamicSchemaBuilder setOptionalPermissions(Iterable<String> permissions) {
170 this.optionalPermissions = permissions;
171 return this;
172 }
173
174 public DynamicSchemaBuilder setComplexType(String complexType) {
175 this.complexType = complexType;
176 return this;
177 }
178
179 public DynamicSchemaBuilder setMaxOccurs(String maxOccurs) {
180 this.maxOccurs = maxOccurs;
181 return this;
182 }
183
184 public DynamicSchemaBuilder setResourceLoader(AlternativeResourceLoader resourceLoader) {
185 this.resourceLoader = resourceLoader;
186 return this;
187 }
188
189 public DynamicSchemaBuilder setTransformer(SchemaTransformer schemaTransformer) {
190 this.schemaTransformer = schemaTransformer;
191 return this;
192 }
193
194 public boolean validate() {
195 return resourceLoader.getResource(path) != null;
196 }
197
198 public DocumentBasedSchema build() {
199 checkNotNull(elementName);
200 checkNotNull(fileName);
201 checkNotNull(complexType);
202 checkNotNull(resourceLoader);
203 checkNotNull(requiredPermissions);
204 checkNotNull(optionalPermissions);
205 return new DocumentBasedSchema(elementName, name, description, path, fileName, complexType, maxOccurs,
206 requiredPermissions, optionalPermissions, resourceLoader,
207 schemaTransformer);
208 }
209 }
210
211 private static Document getDocument(AlternativeResourceLoader resourceLoader, String path, SchemaTransformer transformer) {
212 final URL sourceUrl = resourceLoader.getResource(path);
213 if (sourceUrl == null) {
214 throw new IllegalStateException("Cannot find schema document " + path);
215 }
216 return transformer.transform(parseDocument(sourceUrl));
217 }
218
219 private static Document parseDocument(URL xmlUrl) {
220 Document source;
221 try {
222 source = createSecureSaxReader().read(xmlUrl);
223 } catch (DocumentException e) {
224 throw new IllegalArgumentException("Unable to parse XML", e);
225 }
226
227 return source;
228 }
229
230 public static SAXReader createSecureSaxReader() {
231 return new SAXReader(SecureXmlParserFactory.newXmlReader(), false);
232 }
233 }