PageRenderTime 30ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/andya/confluence/plugins/metadata/MetadataUtils.java

https://bitbucket.org/jwalton/metadata-confluence-plugin
Java | 389 lines | 343 code | 10 blank | 36 comment | 15 complexity | 9431c3c27bb3ccc8ef8c3313bf8d1309 MD5 | raw file
  1. /*
  2. * Copyright (c) 2006, 2007 Andy Armstrong, Kelsey Grant and other contributors.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright notice,
  11. * this list of conditions and the following disclaimer in the documentation
  12. * and/or other materials provided with the distribution.
  13. * * The names of contributors may not
  14. * be used to endorse or promote products derived from this software without
  15. * specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  21. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  24. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. package org.andya.confluence.plugins.metadata;
  29. import com.atlassian.confluence.core.ContentEntityObject;
  30. import com.atlassian.confluence.core.ContentPropertyManager;
  31. import com.atlassian.confluence.core.ConfluenceEntityObject;
  32. import com.atlassian.confluence.renderer.PageContext;
  33. import com.atlassian.renderer.RenderContext;
  34. import com.atlassian.renderer.RenderContextOutputType;
  35. import com.thoughtworks.xstream.XStream;
  36. import org.andya.confluence.plugins.metadata.model.MetadataContent;
  37. import org.andya.confluence.plugins.metadata.model.MetadataValue;
  38. import org.andya.confluence.utils.ContentService;
  39. import java.util.*;
  40. /**
  41. * Useful utilities for dealing with Confluence metadata.
  42. */
  43. public class MetadataUtils {
  44. public static final String METADATA_KEY = "metadata.";
  45. public static final String METADATA_NAMES_KEY = "metadatakeys";
  46. private static final String SEARCHABLE_PROPERTIES = "scaffold!searchableProps";
  47. public static final char METADATA_KEYS_SEPARATOR = '|';
  48. private static XStream xStream = null;
  49. /**
  50. * Returns the property key used for the specified metadata value.
  51. */
  52. public static String getMetadataKey(String valueName) {
  53. return METADATA_KEY + valueName.trim();
  54. }
  55. /**
  56. * Returns an array of the metadata names associated with the given content entity object.
  57. */
  58. public static String[] getMetadataNames(ContentService contentService, ContentEntityObject ceo) {
  59. ContentPropertyManager contentPropertyManager = contentService.getContentPropertyManager();
  60. String metadataKeysString = getMetadataNamesString(contentPropertyManager, ceo);
  61. String[] metadataKeys;
  62. if (metadataKeysString == null || metadataKeysString.length() == 0)
  63. metadataKeys = new String[0];
  64. else
  65. metadataKeys = metadataKeysString.split("\\" + METADATA_KEYS_SEPARATOR);
  66. String[] scaffoldingKeys = getScaffoldingNames(contentPropertyManager, ceo);
  67. String[] allKeys = new String[metadataKeys.length + scaffoldingKeys.length];
  68. System.arraycopy(metadataKeys, 0, allKeys, 0, metadataKeys.length);
  69. System.arraycopy(scaffoldingKeys, 0, allKeys, metadataKeys.length, scaffoldingKeys.length);
  70. return allKeys;
  71. }
  72. /**
  73. * Returns a string of the metadata names associated with the given content entity object.
  74. */
  75. public static String getMetadataNamesString(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo) {
  76. return contentPropertyManager.getTextProperty(ceo, METADATA_NAMES_KEY);
  77. }
  78. /**
  79. * Returns an array of all the metadata values from the specified content entity object.
  80. */
  81. public static MetadataValue[] getMetadataValues(ContentService contentService, ContentEntityObject ceo) {
  82. String[] names = MetadataUtils.getMetadataNames(contentService, ceo);
  83. int count = names.length;
  84. MetadataValue[] values = new MetadataValue[count];
  85. for (int i = 0; i < count; i++) {
  86. String name = names[i];
  87. Object value = MetadataUtils.getMetadataValue(contentService, ceo, name, "");
  88. values[i] = contentService.createMetadataValue(name, value);
  89. }
  90. return values;
  91. }
  92. /**
  93. * Register that the specified content entity object uses a metadata name.
  94. *
  95. * @param contentPropertyManager The content property manager.
  96. * @param ceo The content object to attach metadata to.
  97. * @param valueName The name of the metadata value.
  98. */
  99. public static void registerMetadataName(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo,
  100. String valueName) {
  101. String metadataNamesString = contentPropertyManager.getTextProperty(ceo, METADATA_NAMES_KEY);
  102. String newMetadataNamesString = addMetadataName(metadataNamesString, valueName);
  103. if (!newMetadataNamesString.equals(metadataNamesString))
  104. contentPropertyManager.setTextProperty(ceo, METADATA_NAMES_KEY, newMetadataNamesString);
  105. }
  106. /**
  107. * Clears any metadata names that are registered with the specified content entity object.
  108. *
  109. * @param contentPropertyManager The content property manager.
  110. * @param ceo The content object to attach metadata to.
  111. */
  112. public static void clearMetadataNames(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo) {
  113. contentPropertyManager.removeProperty(ceo, METADATA_NAMES_KEY);
  114. }
  115. public static Map<String,String> getAllMetadata(ContentService contentService, ContentEntityObject ceo) {
  116. Map<String, String> results = new HashMap<String, String>();
  117. List<String> names = getMetadataNames(contentService.getContentPropertyManager(), ceo);
  118. for(String name : names) {
  119. MetadataValue v = getMetadataValue(contentService, ceo, name, null);
  120. results.put(name, v.getValue().toString());
  121. }
  122. return results;
  123. }
  124. /**Clears any metadata and unregister metadata names for a content entity
  125. *
  126. * @param contentPropertyManager The content property manager.
  127. * @param ceo The content object to attach metadata to.
  128. * */
  129. public static void clearMetadata(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo) {
  130. List<String> names = getMetadataNames(contentPropertyManager, ceo);
  131. for(String name : names) {
  132. contentPropertyManager.removeProperty(ceo, getMetadataKey(name));
  133. }
  134. contentPropertyManager.removeProperty(ceo, METADATA_NAMES_KEY);
  135. }
  136. public static List<String> getMetadataNames(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo) {
  137. List<String> names = new ArrayList<String>();
  138. String metadataNamesString = contentPropertyManager.getTextProperty(ceo, METADATA_NAMES_KEY);
  139. if (metadataNamesString != null && metadataNamesString.length() > 0)
  140. names = Arrays.asList(metadataNamesString.split("\\" + METADATA_KEYS_SEPARATOR));
  141. return names;
  142. }
  143. public static String addMetadataName(String namesString, String name) {
  144. if (name == null || name.length() == 0)
  145. return namesString;
  146. if (namesString == null || namesString.length() == 0)
  147. return name;
  148. List<String> names = Arrays.asList(namesString.split("\\" + METADATA_KEYS_SEPARATOR));
  149. if (!names.contains(name))
  150. return namesString + METADATA_KEYS_SEPARATOR + name;
  151. else
  152. return namesString;
  153. }
  154. /**
  155. * Returns a named metadata value from the specified content object. The value is returned
  156. * as unrendered Wiki content so that it can be rendered appropriately on another page.
  157. *
  158. * @param contentService The content service.
  159. * @param ceo The content object to return the metadata for.
  160. * @param valueName The name of the metadata value.
  161. * @param defaultValue The default value to return if no value is found.
  162. * @return The unrendered Wiki content for the metadata value.
  163. */
  164. public static MetadataValue getMetadataValue(ContentService contentService, ConfluenceEntityObject ceo,
  165. String valueName, Object defaultValue) {
  166. Object value = null;
  167. if (ceo instanceof ContentEntityObject) {
  168. value = getRawMetadataValue(contentService, (ContentEntityObject)ceo, valueName, null);
  169. if (value == null)
  170. value = getScaffoldingValue(contentService, (ContentEntityObject)ceo, valueName, null);
  171. }
  172. if (value == null)
  173. value = contentService.getContentValue(ceo, valueName);
  174. if (value instanceof MetadataValue)
  175. return (MetadataValue)value;
  176. return contentService.createMetadataValue(valueName, value, defaultValue);
  177. }
  178. /**
  179. * Returns a named metadata value from the specified content object. The value is returned
  180. * as unrendered Wiki content so that it can be rendered appropriately on another page.
  181. *
  182. * @param contentService The content service.
  183. * @param ceo The content object to return the metadata for.
  184. * @param valueName The name of the metadata value.
  185. * @param defaultValue The default value to return if no value is found.
  186. * @return The unrendered Wiki content for the metadata value.
  187. */
  188. public static MetadataValue getRawMetadataValue(ContentService contentService, ContentEntityObject ceo,
  189. String valueName, Object defaultValue) {
  190. if (ceo == null)
  191. throw new IllegalArgumentException("Attempted to fetch metadata value for null ContentEntityObject");
  192. String key = getMetadataKey(valueName);
  193. ContentPropertyManager contentPropertyManager = contentService.getContentPropertyManager();
  194. Object value = contentPropertyManager != null ? contentPropertyManager.getTextProperty(ceo, key) : null;
  195. return contentService.createMetadataValue(valueName, value, defaultValue);
  196. }
  197. /**
  198. * Sets a named metadata value for the specified content object. The value specified should
  199. * be unrendered Wiki content so that the user of the value can choose how to render it.
  200. *
  201. * @param contentPropertyManager The content property manager.
  202. * @param ceo The content object to attach metadata to.
  203. * @param valueName The name of the metadata value.
  204. * @param unrenderedValue The unrendered Wiki content to be stored.
  205. */
  206. public static void setMetadataValue(ContentPropertyManager contentPropertyManager, ContentEntityObject ceo,
  207. String valueName, String unrenderedValue) {
  208. if (ceo == null)
  209. throw new IllegalArgumentException("Attempted to set metadata value for null ContentEntityObject");
  210. String key = getMetadataKey(valueName);
  211. contentPropertyManager.setTextProperty(ceo, key, unrenderedValue);
  212. registerMetadataName(contentPropertyManager, ceo, valueName);
  213. }
  214. /**
  215. * Sets a named metadata value for the content object associated with the current render context.
  216. * The value specified should be unrendered Wiki content so that the user of the value can choose
  217. * how to render it.
  218. *
  219. * @param renderContext The render context in which this macro is executing.
  220. * @param valueName The name of the metadata value.
  221. * @param unrenderedValue The unrendered Wiki content to be stored.
  222. */
  223. public static void setMetadataValue(ContentPropertyManager contentPropertyManager,
  224. RenderContext renderContext, String valueName, String unrenderedValue) {
  225. String outputType = renderContext.getOutputType();
  226. if(outputType != null && outputType.equals(RenderContextOutputType.PREVIEW))
  227. return;
  228. if (renderContext instanceof PageContext) {
  229. PageContext pageContext = (PageContext)renderContext;
  230. ContentEntityObject ceo = pageContext.getEntity();
  231. // Ensure we're only recording excerpts of the base page and not on templates
  232. if (pageContext.getOriginalContext() == pageContext && ceo != null)
  233. setMetadataValue(contentPropertyManager, ceo, valueName, unrenderedValue);
  234. }
  235. }
  236. /**
  237. * This function creates a collection of {@link MetadataContent} objects for the specified entities.
  238. *
  239. * @param entities
  240. * @return The MetadataContent list.
  241. */
  242. public static List<MetadataContent> getMetadataContent(ContentService contentService, List<ConfluenceEntityObject> entities) {
  243. List<MetadataContent> content = new ArrayList<MetadataContent>();
  244. for (ConfluenceEntityObject entity : entities) {
  245. content.add(new MetadataContent(contentService, entity));
  246. }
  247. return content;
  248. }
  249. /**
  250. * This function returns a list of numbers from the specified {@link MetadataContent}. If matchingName
  251. * and matchingValue are provided, then numbers will only be returned for content with matching values.
  252. *
  253. * @param contents A list of {@link MetadataContent}.
  254. * @param valueName The metadata value to return.
  255. * @param matchingName The name of the metadata to match against.
  256. * @param matchingValue The value for the metadata match.
  257. * @return A list of numbers.
  258. */
  259. public static List<Number> getMetadataNumbers(List<MetadataContent> contents, String valueName,
  260. String matchingName, String matchingValue) {
  261. List<Number> numbers = new ArrayList<Number>();
  262. for (MetadataContent content : contents) {
  263. if (matchingName != null) {
  264. MetadataValue contentMetadataValue = content.getMetadataValue(matchingName);
  265. if (!matchingValue.equals(contentMetadataValue.getWikiSnippet()))
  266. continue;
  267. }
  268. MetadataValue value = content.getMetadataValue(valueName);
  269. if (value != null) {
  270. Number number = value.getValueAsNumber();
  271. if (number != null)
  272. numbers.add(number);
  273. }
  274. }
  275. return numbers;
  276. }
  277. public static String getScaffoldingValue(ContentService contentService, ContentEntityObject content,
  278. String key, String defaultValue) {
  279. if (content == null)
  280. throw new IllegalArgumentException("Attempted to fetch metadata value for null ContentEntityObject");
  281. key = getScaffoldingKey(key);
  282. ContentPropertyManager contentPropertyManager = contentService.getContentPropertyManager();
  283. String value = contentPropertyManager != null ? contentPropertyManager.getTextProperty(content, key) : null;
  284. return value != null ? value : defaultValue;
  285. }
  286. private static String getScaffoldingKey(String key) {
  287. return "scaffold." + key;
  288. }
  289. @SuppressWarnings("unchecked")
  290. public static String[] getScaffoldingNames(ContentPropertyManager contentPropertyManager, ContentEntityObject content) {
  291. Map props = getScaffoldingProperties(contentPropertyManager, content);
  292. if (props == null)
  293. return new String[0];
  294. String[] names = new String[props.size()];
  295. Iterator i = props.keySet().iterator();
  296. int j = 0;
  297. while (i.hasNext()) {
  298. // Trim 'scaffold.' from the property name.
  299. names[j++] = ((String)i.next()).substring(9);
  300. }
  301. return names;
  302. }
  303. @SuppressWarnings("unchecked")
  304. private static Map getScaffoldingProperties(ContentPropertyManager contentPropertyManager, ContentEntityObject content) {
  305. return getMapProperty(contentPropertyManager, content,
  306. SEARCHABLE_PROPERTIES);
  307. }
  308. @SuppressWarnings("unchecked")
  309. private static Map getMapProperty(ContentPropertyManager contentPropertyManager, ContentEntityObject contentEntityObject,
  310. String key) {
  311. return (Map)getObjectProperty(contentPropertyManager, contentEntityObject, key);
  312. }
  313. private static Object getObjectProperty(ContentPropertyManager contentPropertyManager, ContentEntityObject contentEntityObject,
  314. String key) {
  315. String xml = contentPropertyManager.getTextProperty(
  316. contentEntityObject, key);
  317. return xmlToObject(xml);
  318. }
  319. /**
  320. * @return the XStream instance.
  321. */
  322. private static XStream getXStream() {
  323. if (xStream == null) {
  324. xStream = new XStream();
  325. xStream.setClassLoader(MetadataUtils.class.getClassLoader());
  326. }
  327. return xStream;
  328. }
  329. /**
  330. * Converts the object to XML.
  331. *
  332. * @param value The object to convert.
  333. * @return The XML version of the object.
  334. */
  335. protected static String objectToXML(Object value) {
  336. if (value != null)
  337. return getXStream().toXML(value);
  338. return null;
  339. }
  340. /**
  341. * Converts the XML into an object.
  342. *
  343. * @param xml The XML to convert.
  344. * @return the object version of the XML
  345. */
  346. protected static Object xmlToObject(String xml) {
  347. if (xml != null)
  348. return getXStream().fromXML(xml);
  349. return null;
  350. }
  351. }