View Javadoc
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   * Schema based on a XML document resource.
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         // default set of permissions for modules is pretty much unrestricted access to backend and front-end code
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 }