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  {
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         // default set of permissions for modules is pretty much unrestricted access to backend and front-end code
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 }