package org.docear.plugin.bibtex.jabref; import java.io.IOException; import java.util.regex.Pattern; import org.freeplane.core.util.LogUtils; import net.sf.jabref.BasePanel; import net.sf.jabref.BibtexDatabase; import net.sf.jabref.BibtexEntry; import net.sf.jabref.imports.ParserResult; import net.sf.jabref.imports.PostOpenAction; public class DocearTransformForeignDatabaseAction implements PostOpenAction { private final static Pattern COLON_PATTERN = Pattern.compile("(?<!\\\\):", Pattern.MULTILINE); private final static Pattern SEMICOLON_PATTERN = Pattern.compile("(?<!\\\\);", Pattern.MULTILINE); private final static Pattern BACKSLASH_PATTERN = Pattern.compile("(?<!\\\\)\\\\(?![\\\\:;])"); // single # is not allowed in JabRef (except in file or url fields), ## or \# is however // --> uneven numbers of # are also not allowed, but we ignore that - the user will get a JabRef warning anyway private final static Pattern HASH_PATTERN = Pattern.compile("(?<![#\\\\])#(?!#)"); @Override public boolean isActionNecessary(ParserResult pr) { return true; } @Override public void performAction(BasePanel panel, ParserResult pr) { BibtexDatabase db = pr.getDatabase(); for (BibtexEntry entry : db.getEntries()) { for (String field : entry.getAllFields()) { String content = entry.getField(field); if (content != null && content.length()>0) { if (field.equalsIgnoreCase("file")) { try { content = convertFileField(content); } catch(Exception e) { LogUtils.severe("file-field content not well formed: "+content); } } else if (!field.equalsIgnoreCase("url")){ content = HASH_PATTERN.matcher(content).replaceAll("\\\\#"); } entry.setField(field, content); } } } } private String convertFileField(String fileFieldContent) throws ArrayIndexOutOfBoundsException { StringBuffer sb = new StringBuffer(); String[] fileContents = SEMICOLON_PATTERN.split(fileFieldContent); for (int i=0; i<fileContents.length; i++) { if (sb.length() > 0) { sb.append(";"); } try { sb.append(convertSingleFileContent(fileContents[i])); } catch(IOException e) { fileContents[i+1] = fileContents[i]+"\\;"+fileContents[i+1]; } } return sb.toString(); } private String convertSingleFileContent(String content) throws IOException { StringBuffer sb = new StringBuffer(); int typePointer = content.lastIndexOf(":"); if (typePointer < 0) { // file content not complete, maybe due to unescaped semicolons (Zotero style) --> try to add the next field throw new IOException("file content not complete!"); } int pathPointer = content.substring(0, typePointer).lastIndexOf(":"); if (typePointer-pathPointer <= 2) { // file content not complete (MS Windows Drive letter colon is no content separator throw new IOException("file content not complete!"); } //test if this pos is part of a windows path (after a drive letter) and if it is: move further backwards int tmpPointer = content.substring(0, pathPointer).lastIndexOf(':'); if (tmpPointer >= 0) { String possibleDriverLetter = content.substring(tmpPointer, pathPointer); possibleDriverLetter = possibleDriverLetter.replaceAll("\\\\", "").trim(); if (possibleDriverLetter.length()==2) { pathPointer = tmpPointer; } } String type = content.substring(typePointer+1); String path = content.substring(pathPointer+1, typePointer); String description = content.substring(0, pathPointer); sb.append(convertDescription(description)).append(":"); sb.append(convertPath(path)).append(":"); sb.append(type); return sb.toString(); } private String convertDescription(String description) { description = COLON_PATTERN.matcher(description).replaceAll("\\\\:"); description = BACKSLASH_PATTERN.matcher(description).replaceAll("\\\\\\\\"); return description; } private String convertPath(String path) { path = COLON_PATTERN.matcher(path).replaceAll("\\\\:"); path = BACKSLASH_PATTERN.matcher(path).replaceAll("\\\\\\\\"); return path; } }