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