PageRenderTime 25ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/atlassian/uwc/hierarchies/FilepathHierarchy.java

https://bitbucket.org/atlassianlabs/universal-wiki-connector
Java | 312 lines | 221 code | 32 blank | 59 comment | 49 complexity | 6fd8d945402159075b60980a536e9a6f MD5 | raw file
  1. package com.atlassian.uwc.hierarchies;
  2. import java.io.File;
  3. import java.util.Collection;
  4. import java.util.Properties;
  5. import java.util.Set;
  6. import java.util.Vector;
  7. import java.util.regex.Pattern;
  8. import org.apache.log4j.Logger;
  9. import com.atlassian.uwc.ui.Page;
  10. /**
  11. * uses pages' path to set parent-child relationships
  12. *
  13. * @author Laura Kolker
  14. *
  15. */
  16. public class FilepathHierarchy implements HierarchyBuilder {
  17. private static final String PROPKEY_EXT = "filepath-hierarchy-ext";
  18. private static final String PROPKEY_MATCHPAGENAME = "filepath-hierarchy-matchpagename";
  19. private static final String DEFAULT_MATCHPAGENAME = "true";
  20. Logger log = Logger.getLogger(this.getClass());
  21. private HierarchyNode root;
  22. private String fileExtension = null;
  23. private String ignorableAncestors = "";
  24. private String matchpagename = "";
  25. Properties properties = new Properties();
  26. public HierarchyNode buildHierarchy(Collection<Page> pages) {
  27. log.debug("Checking Pages object is valid.");
  28. //check that pages is valid and non-empty
  29. if (pages == null) {
  30. String message = "--> Cannot build hierarchy. Pages object is null.";
  31. log.debug(message);
  32. return null;
  33. }
  34. if (pages.isEmpty()) {
  35. String message = "--> Cannot build hierarchy. Pages object is empty.";
  36. log.debug(message);
  37. return null;
  38. }
  39. log.debug("--> Pages object is valid.");
  40. //debug messages for properties
  41. log.debug("Filepath properties - ignorable-ancestors: " +
  42. getProperties().getProperty("filepath-hierarchy-ignorable-ancestors"));
  43. log.debug("Filepath properties - ext: " +
  44. getProperties().getProperty(PROPKEY_EXT, ""));
  45. log.debug("Filepath properties - matchpagename: "+
  46. getProperties().getProperty(PROPKEY_MATCHPAGENAME, DEFAULT_MATCHPAGENAME));
  47. this.matchpagename = getProperties().getProperty(PROPKEY_MATCHPAGENAME, DEFAULT_MATCHPAGENAME);
  48. HierarchyNode root = getRootNode();
  49. log.info("Building Hierarchy.");
  50. log.debug("foreach Page in Pages...");
  51. for (Page page : pages) {
  52. if (page == null) {
  53. log.debug(".. page is null!");
  54. continue;
  55. }
  56. log.debug(".. page: " + page.getName());
  57. buildRelationships(page, root);
  58. }
  59. return root;
  60. }
  61. /**
  62. * builds all the child parent relationships for the given page,
  63. * rooted in the given root node.
  64. * @param page
  65. * @param root
  66. */
  67. protected void buildRelationships(Page page, HierarchyNode root) {
  68. log.debug(".. Building Relationships.");
  69. String currentPagePath = page.getPath();
  70. String currentPageName = page.getName();
  71. String extension = getFileExtension(currentPageName);
  72. String origPageName = getOrigPagename(page, currentPageName, extension);
  73. Vector<String> ancestorsNotRoot = getAncestors(currentPagePath);
  74. HierarchyNode parent = root;
  75. for (String childString : ancestorsNotRoot) {
  76. if ("".equals(childString)) continue;
  77. log.debug(".... ancestor string = " + childString);
  78. HierarchyNode child = null;
  79. childString += extension;
  80. if (hasExistingRelationship(parent, childString))
  81. child = getChildNode(parent, childString);
  82. else
  83. child = createChildNode(parent, childString);
  84. parent = child;
  85. }
  86. log.debug(".... creating leaf: " + currentPageName);
  87. if (hasExistingRelationship(parent, origPageName)) {
  88. HierarchyNode child = getChildNode(parent, origPageName);
  89. if (child == null) {
  90. String fullname = page.getPath() +
  91. (page.getPath().endsWith(File.separator)?"":File.separator) +
  92. page.getName();
  93. log.warn("Problem assigning page '" + fullname +
  94. "' to a node. Skipping.\n" +
  95. "NOTE: Check for duplicate page names, especially case sensitive " +
  96. "naming conventions. Confluence requires that all pages in the same space " +
  97. "have unique names, and case sensitive page titles will not be preserved.");
  98. return;
  99. }
  100. if (!origPageName.equals(currentPageName))
  101. child.setName(currentPageName);
  102. child.setPage(page);
  103. }
  104. else {
  105. createChildNode(parent, page);
  106. }
  107. }
  108. protected String getOrigPagename(Page page, String currentPageName, String extension) {
  109. String origPageName = (page.getFile() != null)?page.getFile().getName():currentPageName;
  110. String end = "[.][^.]+$"; //any ext
  111. origPageName = origPageName.replaceFirst(end, extension);
  112. if ("".equals(origPageName)) origPageName = currentPageName; //unit tests
  113. return origPageName;
  114. }
  115. /**
  116. * vector of path strings, in order
  117. * @param path Example: Food/Fruit/Apple
  118. * @return Vector of Strings:
  119. * <br/>
  120. * "Food", "Fruit", "Apple"
  121. */
  122. protected Vector<String> getAncestors(String path) {
  123. Properties props = getProperties();
  124. if (props.containsKey("filepath-hierarchy-ignorable-ancestors")) {
  125. String ignorable = props.getProperty("filepath-hierarchy-ignorable-ancestors");
  126. path = removePrefix(path, ignorable, File.separator);
  127. }
  128. log.debug("...... getting ancestors from: '" + path + "'");
  129. String seperator = getSeperator();
  130. String[] ancArray = path.split(seperator);
  131. Vector<String> ancestors = new Vector<String>(ancArray.length);
  132. for (int i = 0; i < ancArray.length; i++) {
  133. String ancestor = ancArray[i];
  134. ancestors.add(ancestor);
  135. }
  136. return ancestors;
  137. }
  138. protected String removePrefix(String fullpath, String prefix, String separator) {
  139. if (prefix.endsWith(separator) && !fullpath.endsWith(separator)) fullpath += separator;
  140. if (fullpath.startsWith(prefix)) fullpath = fullpath.replaceFirst("\\Q"+prefix+"\\E", "");
  141. if (fullpath.startsWith(separator)) fullpath = fullpath.substring(1);
  142. log.debug("removed prefix: " + fullpath);
  143. return fullpath;
  144. }
  145. /**
  146. * @param parent
  147. * @param childname
  148. * @return true if parent node has a child with the given childname
  149. */
  150. protected boolean hasExistingRelationship(HierarchyNode parent, String childname) {
  151. if (parent == null) {
  152. log.debug("...... -> parent is null.");
  153. return false;
  154. }
  155. log.debug("...... checking parent '" + parent.getName() + "' has relationship with '" + childname + "'");
  156. boolean relationship = false;
  157. Collection<HierarchyNode> children = parent.getChildren();
  158. if (children == null || children.isEmpty()) {
  159. log.debug("...... -> parent has no children");
  160. return false;
  161. }
  162. HierarchyNode child = null;
  163. if ("true".equals(matchpagename))
  164. child = parent.findChildByFilename(childname);
  165. else
  166. child = parent.findChild(childname);
  167. if (child == null) relationship = false;
  168. else relationship = true;
  169. log.debug("...... -> " + relationship);
  170. return relationship;
  171. }
  172. /**
  173. * creates a node, with the parent and name info set.
  174. * @param parent
  175. * @param childname
  176. * @return new node
  177. */
  178. protected HierarchyNode createChildNode(HierarchyNode parent, String childname) {
  179. log.debug("...... Creating new child node for: " + childname);
  180. log.debug("...... Page not set.");
  181. HierarchyNode child = new HierarchyNode();
  182. child.setName(childname);
  183. child.setParent(parent);
  184. parent.addChild(child);
  185. return child;
  186. }
  187. /**
  188. * creates a new node with all node fields set
  189. * @param parent
  190. * @param childPage
  191. */
  192. protected HierarchyNode createChildNode(HierarchyNode parent, Page childPage) {
  193. log.debug("...... Creating new child node for: " + childPage.getName());
  194. log.debug("...... Page set.");
  195. HierarchyNode child = new HierarchyNode(childPage, parent);
  196. return child;
  197. }
  198. /**
  199. * gets a node from the parent node that has a name matching the
  200. * given childname
  201. * @param parent
  202. * @param childname
  203. * @return child node or null if none exist
  204. */
  205. protected HierarchyNode getChildNode(HierarchyNode parent, String childname) {
  206. log.debug("...... Getting child node with name: " + childname);
  207. Collection<HierarchyNode> children = (Collection<HierarchyNode>) parent.getChildren();
  208. if (children == null || children.isEmpty()) {
  209. log.debug("...... -> parent has no children. Returning null.");
  210. return null;
  211. }
  212. for (HierarchyNode child : children) {
  213. String thisname = null;
  214. if ("true".equals(matchpagename)) {
  215. thisname = HierarchyNode.getFilename(child);
  216. }
  217. else
  218. thisname = child.getName();
  219. log.debug("...... -> comparing child with: " + thisname);
  220. if (childname.equalsIgnoreCase(thisname)) {
  221. log.debug("...... -> found child.");
  222. return child;
  223. }
  224. }
  225. log.debug("...... -> no child node with that name found.");
  226. return null;
  227. }
  228. /**
  229. * @return the root node
  230. */
  231. protected HierarchyNode getRootNode() {
  232. if (this.root == null) {
  233. this.root = new HierarchyNode();
  234. }
  235. return this.root;
  236. }
  237. protected void clearRootNode() {
  238. this.root = null; //useful for junit tests
  239. }
  240. /**
  241. * @return returns the system file separator
  242. */
  243. private String getSeperator() {
  244. String separator = File.separator;
  245. if ("\\".equals(separator)) {
  246. separator = "\\\\"; // Escape backslashes!
  247. }
  248. return separator;
  249. }
  250. /**
  251. * gets the file extension of the file in the given path
  252. * @param path Example: abc/def.txt
  253. * @return Example .txt
  254. */
  255. protected String getFileExtension(String path) {
  256. //only attempt to discover file extension if we haven't bothered already
  257. if (this.fileExtension != null)
  258. return this.fileExtension;
  259. //first check to see if it was explicitly set as a property. Use that, if it's there.
  260. Properties props = getProperties();
  261. if (props != null && props.containsKey(PROPKEY_EXT)) {
  262. this.fileExtension = props.getProperty(PROPKEY_EXT, "");
  263. return this.fileExtension;
  264. }
  265. if (path == null) return null;
  266. //if path does not have an extension, return nothing
  267. if (!path.matches("([^.]+\\.)+[^.]+")) return "";
  268. //get extension
  269. String replacement = path.replaceFirst(".*?(\\.[^.]*)$", "$1");
  270. log.debug("...... extension discovered: " + replacement );
  271. this.fileExtension = replacement;
  272. return this.fileExtension;
  273. }
  274. public Properties getProperties() {
  275. return this.properties;
  276. }
  277. public void setProperties(Properties properties) {
  278. this.properties = properties;
  279. }
  280. }