import java.io._; import java.net._; import scala.xml._; object deliciousBibtex { val deliciousDataFilepath = System.getProperty("user.home") + "/Library/Application Support/Delicious Library/Library Media Data.xml"; val dlSplitChar:String = System.getProperty("line.separator"); def reverseArray[T](data:Array[T]):unit = { var i:int = 0; var j:int = data.length - 1; while(i < j) { val tmp:T = data(i); data(i) = data(j); data(j) = tmp; i = i + 1; j = j - 1; } } def asList[T](data:Array[T]):List[T] = { var i:int = data.length - 1; var result:List[T] = Nil; while(i >= 0) { result = data(i) :: result; i = i - 1 } result } def bibtexAuthor(dlAuthor:String):List[String] = { val dlSplit:Array[String] = dlAuthor.split("\\s"); reverseArray[String](dlSplit); asList[String](dlSplit); } def bibtexAuthors(dlAuthors:String):List[List[String]] = { val authors:List[String] = asList[String](dlAuthors.split(dlSplitChar)); authors.map(bibtexAuthor) } def bibEntry(node:Elem):String = { var sb:StringBuilder = new StringBuilder(); try { sb.append("@book{"); val authors:List[List[String]] = bibtexAuthors(node.attribute("author")); val published:String = node.attribute("published"); val yearPublished:String = if (published != null) { val publishedSplit:Array[String] = published.split("-"); publishedSplit(publishedSplit.length - 1) } else null; sb.append(authors(0)(0)); if (yearPublished != null) sb.append(yearPublished); sb.append(",\n"); sb.append("\tAuthor={"); sb.append( authors.map[String] (xs => xs.reduceLeft[String]((as,bs) => as + ", " + bs)). reduceLeft[String]((as,bs) => as + " and " + bs)); sb.append("},\n"); sb.append("\tTitle={"); sb.append(node.attribute("title")); sb.append("},\n"); if (yearPublished != null) { sb.append("\tYear={"); sb.append(yearPublished); sb.append("},\n") } sb.append("\tPublisher={"); sb.append(node.attribute("publisher")); sb.append("},\n"); val genre:List[String] = asList[String](node.attribute("genre").split(dlSplitChar)); sb.append("\tKeywords={"); sb.append(genre.reduceLeft[String]((as,bs) => as + ", " + bs)); sb.append("}\n"); sb.append("}\n\n") } catch { case e:Exception => sb = null; System.err.println("Couldn't compose entry for xml " + node.toString()); System.err.println("Skipping and continuing...") } if (sb != null) sb.toString() else "" } def main(args:Array[String]):unit = { val outPath = if (args.length > 0) args(0) else "/tmp/delicious.bib"; try { val writer:FileWriter = new FileWriter(outPath); try { writer.write("%% Created using deliciousLibraryBibTexExporter.\n\n"); for(val items:Elem <- XML.loadFile(deliciousDataFilepath).child.toList; val node:Elem <- items.child.toList; node match { case Elem(_,"book", _, _, _*) => true case _ => false } ) writer.write(bibEntry(node)) } catch { case e: IOException => e.printStackTrace(); System.err.println("IO error while writing output to " + outPath); System.exit(-1) } finally { writer.close(); } } catch { case e: IOException => e.printStackTrace(); System.err.println("Cannot create output file " + outPath); System.exit(-1) } } }