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