PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/amdatu/idea/templating/CapabilityBasedTemplate.java

https://bitbucket.org/amdatu/amdatu-idea
Java | 345 lines | 270 code | 50 blank | 25 comment | 57 complexity | 41ad330b01c9eca26698800b69adfe75 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Copyright (c) Neil Bartlett (2009, 2017) and others.
  2. // All Rights Reserved.
  3. package org.amdatu.idea.templating;
  4. import aQute.bnd.osgi.resource.ResourceUtils;
  5. import aQute.bnd.service.RepositoryPlugin;
  6. import aQute.lib.io.IO;
  7. import org.apache.felix.metatype.MetaData;
  8. import org.apache.felix.metatype.MetaDataReader;
  9. import org.apache.felix.metatype.OCD;
  10. import org.bndtools.templating.BytesResource;
  11. import org.bndtools.templating.FolderResource;
  12. import org.bndtools.templating.Resource;
  13. import org.bndtools.templating.ResourceMap;
  14. import org.bndtools.templating.Template;
  15. import org.bndtools.templating.TemplateEngine;
  16. import org.bndtools.templating.util.AttributeDefinitionImpl;
  17. import org.bndtools.templating.util.ObjectClassDefinitionImpl;
  18. import org.eclipse.core.runtime.IProgressMonitor;
  19. import org.eclipse.core.runtime.NullProgressMonitor;
  20. import org.osgi.framework.Version;
  21. import org.osgi.resource.Capability;
  22. import org.osgi.service.metatype.AttributeDefinition;
  23. import org.osgi.service.metatype.ObjectClassDefinition;
  24. import org.osgi.service.repository.ContentNamespace;
  25. import java.io.File;
  26. import java.io.FileInputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.net.URI;
  30. import java.util.Collections;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Map.Entry;
  34. import java.util.jar.JarEntry;
  35. import java.util.jar.JarFile;
  36. import java.util.jar.JarInputStream;
  37. public class CapabilityBasedTemplate implements Template {
  38. private static final String DEFAULT_DIR = "template/";
  39. private static final String IDENTITY_NAMESPACE = "osgi.identity";
  40. private final Capability capability;
  41. private final TemplateEngine engine;
  42. private RepositoryPlugin repository;
  43. private final String name;
  44. private final String category;
  45. private final String description;
  46. private final Version version;
  47. private final String dir;
  48. private final URI iconUri;
  49. private final String metaTypePath;
  50. private final String ocdRef;
  51. private final String helpPath;
  52. private File _bundleFile = null;
  53. private ResourceMap _inputResources = null;
  54. public CapabilityBasedTemplate(Capability capability, TemplateEngine engine, RepositoryPlugin repository) {
  55. this.capability = capability;
  56. this.engine = engine;
  57. this.repository = repository;
  58. Map<String, Object> attrs = capability.getAttributes();
  59. Object nameObj = attrs.get("name");
  60. this.name = nameObj instanceof String ? (String) nameObj : "<<unknown>>";
  61. this.description = "from " + ResourceUtils.getIdentityCapability(capability.getResource()).osgi_identity();
  62. Object categoryObj = attrs.get("category");
  63. category = categoryObj instanceof String ? (String) categoryObj : null;
  64. // Get version from the capability if found, otherwise it comes from the bundle
  65. Object versionObj = attrs.get("version");
  66. if (versionObj instanceof Version)
  67. this.version = (Version) versionObj;
  68. else if (versionObj instanceof String)
  69. this.version = Version.parseVersion((String) versionObj);
  70. else {
  71. String v = ResourceUtils.getIdentityVersion(capability.getResource());
  72. this.version = v != null ? Version.parseVersion(v) : Version.emptyVersion;
  73. }
  74. Object dirObj = attrs.get("dir");
  75. if (dirObj instanceof String) {
  76. String dirStr = ((String) dirObj).trim();
  77. if (dirStr.charAt(dirStr.length() - 1) != '/')
  78. dirStr += '/';
  79. this.dir = dirStr;
  80. }
  81. else {
  82. this.dir = DEFAULT_DIR;
  83. }
  84. Object iconObj = attrs.get("icon");
  85. iconUri = iconObj instanceof String ? URI.create((String) iconObj) : null;
  86. Object helpObj = attrs.get("help");
  87. helpPath = helpObj instanceof String ? (String) helpObj : null;
  88. Object metaTypeObj = attrs.get("metaType");
  89. metaTypePath = metaTypeObj instanceof String ? (String) metaTypeObj : null;
  90. Object ocdObj = attrs.get("ocd");
  91. ocdRef = ocdObj instanceof String ? ((String) ocdObj).trim() : null;
  92. }
  93. @Override
  94. public String getName() {
  95. return name;
  96. }
  97. @Override
  98. public String getCategory() {
  99. return category;
  100. }
  101. @Override
  102. public String getShortDescription() {
  103. return description;
  104. }
  105. @Override
  106. public Version getVersion() {
  107. return version;
  108. }
  109. @Override
  110. public int getRanking() {
  111. Object rankingObj = capability.getAttributes().get("ranking");
  112. return rankingObj instanceof Number ? ((Number) rankingObj).intValue() : 0;
  113. }
  114. @Override
  115. public ObjectClassDefinition getMetadata() throws Exception {
  116. return getMetadata(new NullProgressMonitor());
  117. }
  118. @Override
  119. public ObjectClassDefinition getMetadata(IProgressMonitor monitor) throws Exception {
  120. String resourceId = ResourceUtils.getIdentityCapability(capability.getResource()).osgi_identity();
  121. if (metaTypePath != null) {
  122. try (JarFile bundleJarFile = new JarFile(fetchBundle())) {
  123. JarEntry metaTypeEntry = bundleJarFile.getJarEntry(metaTypePath);
  124. try (InputStream entryInput = bundleJarFile.getInputStream(metaTypeEntry)) {
  125. MetaData metaData = new MetaDataReader().parse(entryInput);
  126. @SuppressWarnings("rawtypes")
  127. Map ocdMap = metaData.getObjectClassDefinitions();
  128. if (ocdMap != null) {
  129. if (ocdMap.size() == 1) {
  130. @SuppressWarnings("unchecked")
  131. Entry<String, OCD> entry = (Entry<String, OCD>) ocdMap.entrySet().iterator().next();
  132. // There is exactly one OCD, but if the capability specified the 'ocd' property then it must match.
  133. if (ocdRef == null || ocdRef.equals(entry.getKey()))
  134. return new FelixOCDAdapter(entry.getValue());
  135. // log(IStatus.WARNING, String.format("MetaType entry '%s' from resource '%s' did not contain an Object Class Definition with id '%s'", metaTypePath, resourceId, ocdRef), null);
  136. }
  137. else {
  138. // There are multiple OCDs in the MetaType record, so the capability must have specified the 'ocd' property.
  139. if (ocdRef != null) {
  140. OCD ocd = (OCD) ocdMap.get(ocdRef);
  141. if (ocd != null)
  142. return new FelixOCDAdapter(ocd);
  143. // log(IStatus.WARNING, String.format("MetaType entry '%s' from resource '%s' did not contain an Object Class Definition with id '%s'", metaTypePath, resourceId, ocdRef), null);
  144. }
  145. else {
  146. // log(IStatus.WARNING, String.format("MetaType entry '%s' from resource '%s' contains multiple Object Class Definitions, and no 'ocd' property was specified.", metaTypePath, resourceId), null);
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. // No MetaType could be loaded, so build one automatically from the parameters used in the templates.
  154. ObjectClassDefinitionImpl ocdImpl = new ObjectClassDefinitionImpl(name, description, null);
  155. ResourceMap inputs = getInputSources();
  156. Map<String, String> params = engine.getTemplateParameters(inputs, monitor);
  157. for (Entry<String, String> entry : params.entrySet()) {
  158. AttributeDefinitionImpl ad =
  159. new AttributeDefinitionImpl(entry.getKey(), entry.getKey(), 0, AttributeDefinition.STRING);
  160. if (entry.getValue() != null)
  161. ad.setDefaultValue(new String[] {
  162. entry.getValue()
  163. });
  164. ocdImpl.addAttribute(ad, true);
  165. }
  166. return ocdImpl;
  167. }
  168. @Override
  169. public ResourceMap generateOutputs(Map<String, List<Object>> parameters) throws Exception {
  170. return generateOutputs(parameters, new NullProgressMonitor());
  171. }
  172. @Override
  173. public ResourceMap generateOutputs(Map<String, List<Object>> parameters, IProgressMonitor monitor)
  174. throws Exception {
  175. ResourceMap inputs = getInputSources();
  176. return engine.generateOutputs(inputs, parameters, monitor);
  177. }
  178. @Override
  179. public URI getIcon() {
  180. return iconUri;
  181. }
  182. @Override
  183. public URI getHelpContent() {
  184. URI uri = null;
  185. if (helpPath != null) {
  186. try {
  187. File f = fetchBundle();
  188. uri = new URI("jar:" + f.toURI().toURL() + "!/" + helpPath);
  189. }
  190. catch (Exception e) {
  191. // ignore
  192. }
  193. }
  194. return uri;
  195. }
  196. private synchronized ResourceMap getInputSources() throws IOException {
  197. File bundleFile = fetchBundle();
  198. _inputResources = new ResourceMap();
  199. try (JarInputStream in = new JarInputStream(new FileInputStream(bundleFile))) {
  200. JarEntry jarEntry = in.getNextJarEntry();
  201. while (jarEntry != null) {
  202. String entryPath = jarEntry.getName().trim();
  203. if (entryPath.startsWith(dir)) {
  204. String relativePath = entryPath.substring(dir.length());
  205. if (!relativePath.isEmpty()) { // skip the root folder
  206. Resource resource;
  207. if (relativePath.endsWith("/")) {
  208. // strip the trailing slash
  209. relativePath = relativePath.substring(0, relativePath.length());
  210. resource = new FolderResource();
  211. }
  212. else {
  213. // cannot use IO.collect() because it closes the whole JarInputStream
  214. resource = BytesResource.loadFrom(in);
  215. }
  216. _inputResources.put(relativePath, resource);
  217. }
  218. }
  219. jarEntry = in.getNextJarEntry();
  220. }
  221. }
  222. return _inputResources;
  223. }
  224. private synchronized File fetchBundle() throws IOException {
  225. if (_bundleFile != null && _bundleFile.exists())
  226. return _bundleFile;
  227. Capability idCap = capability.getResource().getCapabilities(IDENTITY_NAMESPACE).get(0);
  228. String id = (String) idCap.getAttributes().get(IDENTITY_NAMESPACE);
  229. Capability contentCap = capability.getResource().getCapabilities(ContentNamespace.CONTENT_NAMESPACE).get(0);
  230. URI location;
  231. Object locationObj = contentCap.getAttributes().get("url");
  232. if (locationObj instanceof URI)
  233. location = (URI) locationObj;
  234. else if (locationObj instanceof String)
  235. location = URI.create((String) locationObj);
  236. else
  237. throw new IOException("Template repository entry is missing url attribute");
  238. if ("file".equals(location.getScheme())) {
  239. _bundleFile = IO.getFile(location.getPath());
  240. return _bundleFile;
  241. }
  242. // Try to locate from the workspace and/or repositories if a BundleLocator was provide
  243. // if (locator != null) {
  244. // String hashStr = (String) contentCap.getAttributes().get(ContentNamespace.CONTENT_NAMESPACE);
  245. // try {
  246. // _bundleFile = locator.locate(id, hashStr, "SHA-256", location);
  247. // if (_bundleFile != null)
  248. // return _bundleFile;
  249. // } catch (Exception e) {
  250. // throw new IOException("Unable to fetch bundle for template: " + getName(), e);
  251. // }
  252. // }
  253. try {
  254. _bundleFile = repository.get(id, new aQute.bnd.version.Version(version.toString()), Collections.emptyMap());
  255. if (_bundleFile != null) {
  256. return _bundleFile;
  257. }
  258. }
  259. catch (Exception e) {
  260. throw new IOException("Unable to fetch bundle for template: " + getName(), e);
  261. }
  262. throw new IOException("Unable to fetch bundle for template: " + getName());
  263. }
  264. @Override
  265. public int hashCode() {
  266. final int prime = 31;
  267. int result = 1;
  268. result = prime * result + ((capability == null) ? 0 : capability.hashCode());
  269. return result;
  270. }
  271. @Override
  272. public boolean equals(Object obj) {
  273. if (this == obj)
  274. return true;
  275. if (obj == null)
  276. return false;
  277. if (getClass() != obj.getClass())
  278. return false;
  279. CapabilityBasedTemplate other = (CapabilityBasedTemplate) obj;
  280. if (capability == null) {
  281. if (other.capability != null)
  282. return false;
  283. }
  284. else if (!capability.equals(other.capability))
  285. return false;
  286. return true;
  287. }
  288. @Override
  289. public void close() throws IOException {
  290. // nothing to do
  291. }
  292. private static void log(int level, String message, Throwable e) {
  293. // Plugin.getDefault().getLog().log(new Status(level, Plugin.PLUGIN_ID, 0, message, e));
  294. }
  295. }