PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/atlassian/uwc/exporters/MoinmoinExporter.java

https://bitbucket.org/bowmanb1/universal-wiki-converter
Java | 413 lines | 308 code | 71 blank | 34 comment | 49 complexity | 1faf94b57425dc386bcf52038f711791 MD5 | raw file
  1. package com.atlassian.uwc.exporters;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.FileOutputStream;
  7. import java.io.FileReader;
  8. import java.io.IOException;
  9. import java.nio.channels.FileChannel;
  10. import java.nio.charset.Charset;
  11. import java.sql.SQLException;
  12. import java.text.SimpleDateFormat;
  13. import java.util.Date;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. import java.util.regex.Matcher;
  17. import java.util.regex.Pattern;
  18. import org.apache.log4j.Logger;
  19. import com.atlassian.uwc.prep.MoinMoinPreparation.PageDirFileFilter;
  20. import com.atlassian.uwc.ui.FileUtils;
  21. /**
  22. * Exporter code based on MoinMoinPreparation class.
  23. * This is partially a refactor, but also contains some new properties and improvements.
  24. */
  25. public class MoinmoinExporter implements Exporter {
  26. private boolean running = false;
  27. Logger log = Logger.getLogger(this.getClass());
  28. private Map properties;
  29. private static final String CURRENT = "current";
  30. private static final String REVISIONS = "revisions";
  31. private static final String BADCONTENT = "BadContent";
  32. private static final String CATEGORY = "CategoryRoot";
  33. private static final String EXTENSION = ".txt";
  34. public static final String REVLOG = "edit-log";
  35. private class RevInfo{
  36. public String userid;
  37. public Date timestamp;
  38. public String revComment;
  39. public Integer revision;
  40. }
  41. public void cancel() {
  42. log.info("Cancelling Moinmoin Export");
  43. this.running = false;
  44. }
  45. public void export(Map propertiesMap) throws ClassNotFoundException,
  46. SQLException {
  47. this.running = true;
  48. log.info("Beginning Moinmoin Export");
  49. this.properties = propertiesMap;
  50. if (validDirectories()) {
  51. exportPages();
  52. }
  53. if (this.running)
  54. log.info("Moinmoin Export Complete");
  55. this.running = false;
  56. }
  57. /**
  58. * @return true if the src and out directories are valid directories
  59. */
  60. protected boolean validDirectories() {
  61. if (this.running == false) return false;
  62. String src = getSrc();
  63. String out = getOut();
  64. if (src == null || out == null) {
  65. log.error("src and out properties must be set in conf/exporter.moinmoin.properties");
  66. return false;
  67. }
  68. File pagesDir = new File(src);
  69. File destinationDir = new File(out);
  70. if (pagesDir.isFile() || !pagesDir.exists()) {
  71. log.error("src directory is not a valid directory: " + src);
  72. return false;
  73. }
  74. if (destinationDir.isFile()) {
  75. log.error("out property is not a directory: " + out);
  76. return false;
  77. }
  78. if (!destinationDir.exists()) {
  79. if (!destinationDir.mkdirs()) {
  80. log.error("Impossible to create out directory: \"" + out + "\".");
  81. return false;
  82. }
  83. }
  84. log.debug("src and out directories are valid");
  85. return true;
  86. }
  87. private String getSrc() {
  88. if (this.properties == null) return "";
  89. return (String) this.properties.get("src");
  90. }
  91. private String getOut() {
  92. if (this.properties == null) return "";
  93. return (String) this.properties.get("out");
  94. }
  95. /**
  96. * export moinmoin pages to out directory
  97. */
  98. private void exportPages() {
  99. if (this.running == false) return;
  100. log.debug("src directory: " + getSrc());
  101. log.debug("out directory: " + getOut());
  102. File pagesDir = new File(getSrc());
  103. String[] pages = pagesDir.list(new PageDirFileFilter());
  104. String current = null;
  105. Map<String, String> usernames = getUserNameInfo(getSrc() + File.separator + ".." );
  106. for (int i = 0; i < pages.length; i++) {
  107. if (!pages[i].startsWith(BADCONTENT) && !pages[i].startsWith(CATEGORY) ) { //ignore BADCONTENT page
  108. log.debug("page: " + pages[i]);
  109. if (exportHistory()) {
  110. final String pagedir = getSrc() + File.separator + pages[i];
  111. final String revisiondir = pagedir + File.separator + REVISIONS;
  112. log.debug("revision dir: " + revisiondir );
  113. String[] revisions = new File(revisiondir).list();
  114. if (revisions == null) {
  115. log.error("Revisions directory was null. Skipping.");
  116. continue;
  117. }
  118. Map<Integer, RevInfo> revmap = readRevisionsInfo(pagedir, usernames);
  119. for (String revision : revisions) {
  120. if (!revision.matches("\\d+")) continue; //ignore non-numbers
  121. int num = Integer.parseInt(revision);
  122. try {
  123. File srcfile = new File(getSrc() + File.separator + pages[i] +
  124. File.separator + REVISIONS + File.separator + revision);
  125. File outfile = new File(getOut() + File.separator + pages[i] + "-" + num + EXTENSION);
  126. copyFile(srcfile,outfile);
  127. //addTimestampData(outfile, new Date(srcfile.lastModified()));
  128. addRevData(outfile, revmap.get(num));
  129. addTitleData(outfile, pages[i]);
  130. } catch (FileNotFoundException e) {
  131. log.info("Page \"" + pages[i] + "\" has been deleted and will be ignored.");
  132. } catch (Exception e) {
  133. e.printStackTrace();
  134. }
  135. }
  136. }
  137. else { //only export current
  138. current = getCurrentRevision(getSrc() + File.separator + pages[i]);
  139. log.debug(pages[i] + "\ncurrent revision: " + current);
  140. if (current != null) {
  141. try {
  142. File srcfile = new File(getSrc() + File.separator + pages[i] +
  143. File.separator + REVISIONS + File.separator + current);
  144. File outfile = new File(getOut() + File.separator + pages[i] + EXTENSION);
  145. copyFile(srcfile, outfile);
  146. addTitleData(outfile, pages[i]);
  147. } catch (FileNotFoundException e) {
  148. log.info("Page \"" + pages[i] + "\" has been deleted and will be ignored.");
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. }
  152. }
  153. }
  154. }
  155. }
  156. }
  157. final Charset charset = Charset.forName("windows-1252");
  158. private Map<String, String> getUserNameInfo(String srcDir) {
  159. Map<String, String> res = new HashMap<String, String>();
  160. String usersrc = srcDir + File.separator + "user";
  161. log.debug("read UserInfo: " + usersrc);
  162. Pattern namefinder = Pattern.compile("^name=(\\S+)", Pattern.MULTILINE);
  163. try{
  164. File userdir = new File(usersrc);
  165. for( File f : userdir.listFiles() ){
  166. log.debug("reading userfile: " + f.getName());
  167. // leave directories out
  168. if( f.isDirectory() ) continue;
  169. String key = f.getName();
  170. String cont = FileUtils.readTextFile(f, charset);
  171. Matcher m = namefinder.matcher(cont);
  172. if(m.find()){
  173. String name = m.group(1);
  174. log.debug(String.format(" Key: %s \t Name %s", key, name));
  175. res.put(key, name);
  176. }
  177. }
  178. } catch (Exception e) {
  179. e.printStackTrace();
  180. }
  181. return res;
  182. }
  183. private void addRevData(File outfile, RevInfo revInfo) {
  184. final String tsData = "{timestamp:" + fm.format(revInfo.timestamp) + "}\n";
  185. final String userid = "{userid:" + revInfo.userid + "}\n";
  186. final String revComment = "{revcomment:" + revInfo.revComment + "}\n";
  187. String filecontents;
  188. try {
  189. filecontents = FileUtils.readTextFile(outfile);
  190. } catch (IOException e) {
  191. log.error("Could not read output file: " + outfile.getAbsolutePath());
  192. e.printStackTrace();
  193. return;
  194. }
  195. String newcontents = tsData + userid + revComment + filecontents;
  196. FileUtils.writeFile(newcontents, outfile.getAbsolutePath());
  197. }
  198. private Map<Integer, RevInfo> readRevisionsInfo(String pagedir, Map<String, String> usermap) {
  199. final File revlogf = new File( pagedir + File.separator + REVLOG);
  200. final Map<Integer, RevInfo> res = new HashMap<Integer, RevInfo>();
  201. log.debug("Read reflog file: " + revlogf.getAbsolutePath() );
  202. try {
  203. final String filecontens = FileUtils.readTextFile(revlogf, charset);
  204. for( String line : filecontens.split("\\n") ){
  205. log.debug("read line of revisions file: " + line);
  206. String[] s = line.split("\\t",9);
  207. RevInfo r = new RevInfo();
  208. r.revision = Integer.parseInt(s[1]);
  209. res.put(r.revision, r);
  210. if(usermap.containsKey(s[6])){
  211. r.userid = usermap.get(s[6]);
  212. } else {
  213. // use the plain number, user was deleted
  214. r.userid = s[6];
  215. }
  216. if(r.userid.trim().equals("")){
  217. r.userid = "SYSTEM";
  218. }
  219. r.revComment = s[8].trim();
  220. long time = Long.parseLong(s[0]) / 1000; //convert micro to milliseconds
  221. r.timestamp = new Date(time);
  222. }
  223. } catch (IOException e) {
  224. // TODO Auto-generated catch block
  225. e.printStackTrace();
  226. }
  227. return res;
  228. }
  229. static private SimpleDateFormat fm = new SimpleDateFormat("yyyyMMddHHmmss");
  230. private void addTimestampData(File outfile, Date timestamp) {
  231. String tsData = "{timestamp:" + fm.format(timestamp) + "}\n";
  232. String filecontents;
  233. try {
  234. filecontents = FileUtils.readTextFile(outfile);
  235. } catch (IOException e) {
  236. log.error("Could not read output file: " + outfile.getAbsolutePath());
  237. e.printStackTrace();
  238. return;
  239. }
  240. String newcontents = tsData + filecontents;
  241. FileUtils.writeFile(newcontents, outfile.getAbsolutePath());
  242. }
  243. protected void addTitleData(File outfile, String title) {
  244. title = title.replaceAll("\\(2f\\)", "/");
  245. String titledata = "{orig-title:" + title + "}\n";
  246. String filecontents;
  247. try {
  248. filecontents = FileUtils.readTextFile(outfile);
  249. } catch (IOException e) {
  250. log.error("Could not read output file: " + outfile.getAbsolutePath());
  251. e.printStackTrace();
  252. return;
  253. }
  254. String newcontents = titledata + filecontents;
  255. FileUtils.writeFile(newcontents, outfile.getAbsolutePath());
  256. }
  257. private boolean exportHistory() {
  258. if (this.properties == null) return false;
  259. return Boolean.parseBoolean((String) this.properties.get("history"));
  260. }
  261. private boolean exportHistoryComments() {
  262. if (this.properties == null) return false;
  263. return Boolean.parseBoolean((String) this.properties.get("histcomment"));
  264. }
  265. /**
  266. * copies file to newFile
  267. * @param file
  268. * @param newFile
  269. * @throws FileNotFoundException
  270. * @throws IOException
  271. */
  272. protected void copyFile(File file, File newFile) throws FileNotFoundException, IOException {
  273. if (!this.running) return;
  274. log.debug("Copying '" + file.getAbsolutePath() + "' to '" + newFile.getAbsolutePath() + "'");
  275. if (!file.exists()) log.error("File doesn't exist. Cannot copy: " + file.getAbsolutePath());
  276. // Create channel on the source
  277. FileChannel srcChannel = new FileInputStream(file.getAbsolutePath()).getChannel();
  278. // Create channel on the destination
  279. FileChannel dstChannel = new FileOutputStream(newFile.getAbsolutePath()).getChannel();
  280. // Copy file contents from source to destination
  281. int buffersize = -1;
  282. try {
  283. //see if the user specified a buffer size
  284. String buffersizeStr = (String) this.properties.get("buffer-size");
  285. if (buffersizeStr != null) {
  286. try {
  287. buffersize = Integer.parseInt(buffersizeStr);
  288. }
  289. catch (NumberFormatException en) {
  290. log.error("Property buffer-size is not an integer. Using filesize.");
  291. }
  292. }
  293. if (buffersize > 0) { //user set buffersize - see Michael Grove's code in UWC-349
  294. long size = srcChannel.size();
  295. long position = 0;
  296. while (position < size) {
  297. position += srcChannel.transferTo(position, buffersize, dstChannel);
  298. }
  299. }
  300. else { //if no user specified buffer size, use filesize
  301. dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
  302. }
  303. } catch (FileNotFoundException e) {
  304. throw e;
  305. } catch (IOException e2) {
  306. throw e2;
  307. } catch (RuntimeException e3) {
  308. throw e3;
  309. } finally {
  310. // Close the channels
  311. srcChannel.close();
  312. dstChannel.close();
  313. if (!newFile.exists()) log.error("Copying file unsuccessful. New file does not exist: " + newFile.getAbsolutePath());
  314. }
  315. }
  316. /**
  317. * getCurrentRevision
  318. *
  319. * @param pagePath
  320. * @return current revision filename
  321. */
  322. private String getCurrentRevision(String pagePath) {
  323. String current = null;
  324. try {
  325. BufferedReader br = new BufferedReader(new FileReader(pagePath + File.separator + CURRENT));
  326. current = br.readLine();
  327. } catch (FileNotFoundException e) {
  328. log.info("Page \"" + pagePath + "\" has been deleted and will be ignored.");
  329. } catch (IOException e) {
  330. e.printStackTrace();
  331. }
  332. return current;
  333. }
  334. //used with unit testing
  335. protected void setProperties(Map properties) {
  336. this.properties = properties;
  337. }
  338. //used with unit testing
  339. protected void setRunning(boolean running) {
  340. this.running = running;
  341. }
  342. }