package org.docear.plugin.bibtex.jabref;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.ws.rs.core.UriBuilder;
import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.GUIGlobals;
import net.sf.jabref.Globals;
import net.sf.jabref.gui.FileListEntry;
import net.sf.jabref.gui.FileListTableModel;
import net.sf.jabref.labelPattern.LabelPatternUtil;
import org.apache.commons.io.FilenameUtils;
import org.docear.plugin.bibtex.JabRefProjectExtension;
import org.docear.plugin.bibtex.Reference;
import org.docear.plugin.bibtex.Reference.Item;
import org.docear.plugin.bibtex.ReferencesController;
import org.docear.plugin.core.features.DocearMapModelExtension;
import org.docear.plugin.core.features.MapModificationSession;
import org.docear.plugin.core.util.NodeUtilities;
import org.docear.plugin.core.workspace.model.DocearWorkspaceProject;
import org.freeplane.core.util.Compat;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.attribute.Attribute;
import org.freeplane.features.attribute.AttributeController;
import org.freeplane.features.attribute.NodeAttributeTableModel;
import org.freeplane.features.link.LinkController;
import org.freeplane.features.link.LinkModel;
import org.freeplane.features.link.NodeLinkModel;
import org.freeplane.features.link.NodeLinks;
import org.freeplane.features.link.mindmapmode.MLinkController;
import org.freeplane.features.map.INodeView;
import org.freeplane.features.map.MapModel;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.mindmapmode.MModeController;
import org.freeplane.features.url.UrlManager;
import org.freeplane.plugin.workspace.URIUtils;
import org.freeplane.plugin.workspace.WorkspaceController;
import org.freeplane.plugin.workspace.features.WorkspaceMapModelExtension;
import org.freeplane.plugin.workspace.model.project.AWorkspaceProject;
import org.freeplane.view.swing.map.NodeView;
public class JabRefAttributes {
private static Boolean updateNodeLock = false;
private boolean nodeDirty = false;
private HashMap<String, String> valueAttributes = new HashMap<String, String>();
private String keyAttribute;
public JabRefAttributes() {
registerAttributes();
}
public void registerAttributes() {
this.keyAttribute = TextUtils.getText("bibtex_key");
this.valueAttributes.put("authors", "author");
this.valueAttributes.put("title", "title");
this.valueAttributes.put("year", "year");
this.valueAttributes.put("journal", "journal");
}
public String getKeyAttribute() {
return keyAttribute;
}
public HashMap<String, String> getValueAttributes() {
return valueAttributes;
}
public String getBibtexKey(NodeModel node) {
return getAttributeValue(node, this.keyAttribute);
}
public String getAttributeValue(NodeModel node, String attributeName) {
NodeAttributeTableModel attributeTable = (NodeAttributeTableModel) node.getExtension(NodeAttributeTableModel.class);
if (attributeTable == null) {
return null;
}
for (Attribute attribute : attributeTable.getAttributes()) {
if (attribute.getName().equals(attributeName)) {
return attribute.getValue().toString();
}
}
return null;
}
public boolean isReferencing(BibtexEntry entry, NodeModel node) {
String nodeKey = getBibtexKey(node);
String entryKey = entry.getCiteKey();
if (nodeKey != null && entryKey != null && nodeKey.equals(entryKey)) {
return true;
}
return false;
}
public void setReferenceToNode(BibtexEntry entry) throws ResolveDuplicateEntryAbortedException {
NodeModel node = Controller.getCurrentModeController().getMapController().getSelectedNode();
try {
WorkspaceMapModelExtension mapExt = WorkspaceController.getMapModelExtension(node.getMap());
AWorkspaceProject project = mapExt.getProject();
if(project != null) {
JabRefProjectExtension ext = (JabRefProjectExtension) project.getExtensions(JabRefProjectExtension.class);
if(ext != null) {
setReferenceToNode(new Reference(ext.getBaseHandle().getBasePanel(), entry), node);
return;
}
}
setReferenceToNode(new Reference(ReferencesController.getController().getJabrefWrapper().getBasePanel(), entry), node);
}
catch(Exception e) {
LogUtils.warn("JabRefAttributes.setReferenceToNode()");
}
}
public void removeReferenceFromNode(NodeModel node) {
for(INodeView nodeView : node.getViewers()) {
if(nodeView instanceof NodeView) {
NodeAttributeTableModel attributeTable = ((NodeView) nodeView).getAttributeView().getAttributes();
if (attributeTable == null) {
continue;
}
List<String> keys = attributeTable.getAttributeKeyList();
for (String attributeKey : keys) {
if (this.valueAttributes.containsKey(attributeKey) || this.keyAttribute.equals(attributeKey)) {
int pos = attributeTable.getAttributePosition(attributeKey);
if(pos > -1) {
try {
AttributeController.getController(MModeController.getMModeController()).performRemoveRow(attributeTable, pos);
}
catch (Exception e) {
//DOCEAR - ignore for now
//the whole attribute removing is completely messed up
//LogUtils.info("not found ("+attributeKey+"): "+pos);
//LogUtils.warn(e);
}
}
}
}
if(attributeTable.getRowCount() <= 0) {
((NodeView) nodeView).getAttributeView().viewRemoved();
}
}
}
}
@SuppressWarnings("unchecked")
public boolean updateReferenceToNode(Reference reference, NodeModel node) throws ResolveDuplicateEntryAbortedException {
if (updateNodeLock) {
return false;
}
synchronized (updateNodeLock) {
updateNodeLock = true;
}
boolean changes = false;
try {
MapModificationSession session = node.getMap().getExtension(DocearMapModelExtension.class).getMapModificationSession();
Set<String> ignoresPdf = null;
if (session != null) {
ignoresPdf = (Set<String>) session.getSessionObject(MapModificationSession.FILE_IGNORE_LIST);
if (ignoresPdf == null) {
ignoresPdf = new HashSet<String>();
session.putSessionObject(MapModificationSession.FILE_IGNORE_LIST, ignoresPdf);
}
}
Set<String> ignoresUrl = null;
if (session != null) {
ignoresUrl = (Set<String>) session.getSessionObject(MapModificationSession.URL_IGNORE_LIST);
if (ignoresUrl == null) {
ignoresUrl = new HashSet<String>();
session.putSessionObject(MapModificationSession.URL_IGNORE_LIST, ignoresUrl);
}
}
for (URI uri : reference.getUris()) {
File file = UrlManager.getController().getAbsoluteFile(node.getMap(), uri);
URL url = null;
if (file == null) {
try {
url = uri.toURL();
}
catch (MalformedURLException e) {
LogUtils.warn(e);
}
}
try {
if (ignoresPdf != null) {
if (file != null) {
if (ignoresPdf.contains(file.getName())) {
throw new ResolveDuplicateEntryAbortedException(file);
}
}
}
else if (ignoresUrl != null) {
if (url != null) {
if (ignoresUrl.contains(url.toExternalForm())) {
throw new ResolveDuplicateEntryAbortedException(url);
}
}
}
}
catch (NullPointerException e) {
LogUtils.warn("org.docear.plugin.bibtex.jabrefe.JabRefAttributes.updateReferenceToNode: " + e.getMessage());
}
catch (ResolveDuplicateEntryAbortedException ex) {
if (ignoresPdf != null) {
if (file != null) {
ignoresPdf.add(file.getName());
}
}
throw ex;
}
}
NodeUtilities.setAttributeValue(node, reference.getKey().getName(), reference.getKey().getValue(), true);
NodeUtilities.updateAttributeList();
NodeAttributeTableModel attributeTable = (NodeAttributeTableModel) node.getExtension(NodeAttributeTableModel.class);
if (attributeTable == null) {
return false;
}
AttributeController attributeController = AttributeController.getController(MModeController.getMModeController());
Vector<Attribute> attributes = attributeTable.getAttributes();
ArrayList<Item> inserts = new ArrayList<Item>();
for (Item item : reference.getAttributes()) {
boolean found = false;
for (int i = 0; i < attributes.size() && !found; i++) {
try {
Attribute attribute = attributes.get(i);
if (attribute.getName().equals(item.getName())) {
found = true;
if (item.getValue() == null) {
attributeController.performRemoveRow(attributeTable, i);
changes = true;
}
else if (!attribute.getValue().equals(item.getValue())) {
attributeController.performSetValueAt(attributeTable, item.getValue(), i, 1);
attribute.setValue(item.getValue());
changes = true;
}
}
}
catch (Exception e) {
LogUtils.warn("Exception in org.docear.plugin.bibtex.jabref.JabRefAttributes.updateReferenceToNode(): "+ e.getMessage());
}
}
if (!found && item.getValue() != null) {
inserts.add(item);
}
}
int i = attributes.size();
for (Item item : inserts) {
changes = true;
try {
AttributeController.getController(MModeController.getMModeController()).performInsertRow(attributeTable, i, item.getName(), item.getValue());
}
catch (Throwable ignore) {
// probably just another swing with threading issue that can (hopefully) be ignored
}
i++;
}
}
finally {
try {
// do not overwrite existing links
NodeLinks nodeLinks = NodeLinks.getLinkExtension(node);
if (nodeLinks == null || nodeLinks.getHyperLink() == null) {
// add link to node
if (reference.getUris().size() > 0) {
((MLinkController) MLinkController.getController()).setLinkTypeDependantLink(node, reference.getUris().iterator().next());
changes = true;
}
else {
URL url = reference.getUrl();
if (url != null) {
((MLinkController) MLinkController.getController()).setLinkTypeDependantLink(node, URI.create(url.toExternalForm()));
changes = true;
}
}
}
}
finally {
synchronized (updateNodeLock) {
updateNodeLock = false;
}
}
}
return changes;
}
public boolean setReferenceToNode(BibtexEntry entry, NodeModel node) throws ResolveDuplicateEntryAbortedException {
//return setReferenceToNode(new Reference(entry), node);
try {
WorkspaceMapModelExtension mapExt = WorkspaceController.getMapModelExtension(node.getMap());
AWorkspaceProject project = mapExt.getProject();
if(project != null) {
JabRefProjectExtension ext = (JabRefProjectExtension) project.getExtensions(JabRefProjectExtension.class);
if(ext != null) {
return setReferenceToNode(new Reference(ext.getBaseHandle().getBasePanel(), entry), node);
}
}
return setReferenceToNode(new Reference(ReferencesController.getController().getJabrefWrapper().getBasePanel(), entry), node);
}
catch(Exception e) {
LogUtils.warn("JabRefAttributes.setReferenceToNode(): "+e.getMessage());
e.printStackTrace();
return false;
}
}
public boolean setReferenceToNode(Reference reference, NodeModel node) throws ResolveDuplicateEntryAbortedException {
return updateReferenceToNode(reference, node);
}
public void removeFileromBibtexEntry(File file, BibtexEntry entry) {
String filename = file.getName();
FileListTableModel model = new FileListTableModel();
String oldVal = entry.getField(GUIGlobals.FILE_FIELD);
if (oldVal == null) {
return;
}
model.setContent(oldVal);
for (int i = 0; i < model.getRowCount(); i++) {
FileListEntry fle = model.getEntry(i);
File f = new File(fle.getLink());
if (filename.equals(f.getName())) {
model.removeEntry(i);
LogUtils.info(oldVal + " <--> " + model.getStringRepresentation());
i--;
}
}
entry.setField(GUIGlobals.FILE_FIELD, model.getStringRepresentation());
}
public void removeUrlFromBibtexEntry(URL url, BibtexEntry entry) {
String field = entry.getField("url");
if (url != null && field != null && field.equals(url.toString())) {
entry.setField("url", null);
}
}
// public void resolveDuplicateLinks(BibtexEntry entry) throws
// InterruptedException {
// for (String s : retrieveFileLinksFromEntry(entry)) {
// try {
// resolveDuplicateLinks(new File(s));
// }
// catch (Exception ex) {
// LogUtils.warn("org.docear.plugin.bibtex.jabref.JabRefAttributes.resolveDuplicateLinks: "
// + ex.getMessage());
// }
// }
// }
public void removeLinkFromNode(NodeModel node) {
for (LinkModel linkModel : NodeLinks.getLinkExtension(node).getLinks()) {
if (linkModel instanceof NodeLinkModel) {
((MLinkController) LinkController.getController()).removeArrowLink((NodeLinkModel) linkModel);
}
}
}
// FIXME: not used yet --> implement functionality into
// findBibtexEntryForPDF
public BibtexEntry findBibtexEntryForURL(URI nodeUri, MapModel map, boolean ignoreDuplicates) throws ResolveDuplicateEntryAbortedException {
WorkspaceMapModelExtension mapExt = WorkspaceController.getMapModelExtension(map);
if(mapExt == null || mapExt.getProject() == null || !mapExt.getProject().isLoaded()) {
//DOCEAR - todo: what to do?
return null;
}
else {
JabRefProjectExtension prjExt = (JabRefProjectExtension) mapExt.getProject().getExtensions(JabRefProjectExtension.class);
if(prjExt == null) {
return null;
}
else {
ReferencesController.getController().getJabrefWrapper().getJabrefFrame().showBasePanel(prjExt.getBaseHandle().getBasePanel());
}
}
BibtexDatabase database = ReferencesController.getController().getJabrefWrapper().getDatabase();
if (database == null || nodeUri == null) {
return null;
}
MapModificationSession session = map.getExtension(DocearMapModelExtension.class).getMapModificationSession();
Set<String> ignores = null;
if (session != null) {
ignores = (Set<String>) session.getSessionObject(MapModificationSession.URL_IGNORE_LIST);
if (ignores == null) {
ignores = new HashSet<String>();
session.putSessionObject(MapModificationSession.URL_IGNORE_LIST, ignores);
}
}
URL nodeUrl = null;
try {
nodeUrl = nodeUri.toURL();
}
catch (Exception e1) {
LogUtils.info(e1.getMessage());
return null;
}
try {
if (ignores != null) {
if (nodeUrl != null && !nodeUrl.toExternalForm().isEmpty()) {
if (ignores.contains(nodeUrl.toExternalForm())) {
throw new ResolveDuplicateEntryAbortedException(nodeUrl);
}
}
}
if (!ignoreDuplicates) {
DuplicateResolver.getDuplicateResolver().resolveDuplicateLinks(nodeUrl);
}
for (BibtexEntry entry : database.getEntries()) {
String entryUrlField = entry.getField("url");
if (entryUrlField != null && !entryUrlField.isEmpty()) {
URI entryUri = null;
try {
entryUri = URI.create(entryUrlField);
}
catch (Exception e) {
LogUtils.warn("org.docear.plugin.bibtex.jabref.JabRefAttributes.findBibtexEntryForURL: " + e.getMessage());
continue;
}
String entryScheme = entryUri.getScheme();
String nodeScheme = nodeUri.getScheme();
if (entryScheme != null && nodeScheme != null && !entryScheme.equals(nodeScheme)) {
continue;
}
String entryUriString = entryUri.toString();
if (entryScheme != null) {
entryUriString = entryUriString.substring(entryScheme.length() + 3);
}
String nodeUriString = nodeUri.toString();
if (nodeScheme != null) {
nodeUriString = nodeUriString.substring(nodeScheme.length() + 3);
}
if (entryUriString.equals(nodeUriString)) {
return entry;
}
}
}
}
catch (ResolveDuplicateEntryAbortedException e) {
if (ignores != null) {
if (nodeUrl != null && !nodeUrl.toExternalForm().isEmpty()) {
ignores.add(nodeUrl.toExternalForm());
}
}
throw e;
}
return null;
}
public BibtexEntry findBibtexEntryForPDF(URI uri, MapModel map) throws ResolveDuplicateEntryAbortedException {
return findBibtexEntryForPDF(uri, map, false);
}
public BibtexEntry findBibtexEntryForPDF(URI uri, MapModel map, boolean ignoreDuplicates) throws ResolveDuplicateEntryAbortedException {
WorkspaceMapModelExtension mapExt = WorkspaceController.getMapModelExtension(map);
if(mapExt == null || mapExt.getProject() == null || !mapExt.getProject().isLoaded()) {
//DOCEAR - todo: what to do?
return null;
}
else {
JabRefProjectExtension prjExt = (JabRefProjectExtension) mapExt.getProject().getExtensions(JabRefProjectExtension.class);
if(prjExt == null) {
//DOCEAR - todo: what to do?
return null;
}
ReferencesController.getController().getJabrefWrapper().getJabrefFrame().showBasePanel(prjExt.getBaseHandle().getBasePanel());
}
BibtexDatabase database = ReferencesController.getController().getJabrefWrapper().getDatabase();
if (database == null) {
return null;
}
// file name linked in a node
File nodeFile = UrlManager.getController().getAbsoluteFile(map, uri);
if (nodeFile == null || nodeFile.getAbsolutePath().isEmpty()) {
return null;
}
String nodeFileName = nodeFile.getName();
String baseName = FilenameUtils.removeExtension(nodeFileName);
MapModificationSession session = map.getExtension(DocearMapModelExtension.class).getMapModificationSession();
Set<String> ignores = null;
if (session != null) {
ignores = (Set<String>) session.getSessionObject(MapModificationSession.FILE_IGNORE_LIST);
if (ignores == null) {
ignores = new HashSet<String>();
session.putSessionObject(MapModificationSession.FILE_IGNORE_LIST, ignores);
}
}
try {
if (ignores != null) {
if (nodeFileName != null) {
if (ignores.contains(nodeFileName)) {
throw new ResolveDuplicateEntryAbortedException(nodeFile);
}
}
}
if (!ignoreDuplicates) {
DuplicateResolver.getDuplicateResolver().resolveDuplicateLinks(nodeFile);
}
for (BibtexEntry entry : database.getEntries()) {
String jabrefFiles = entry.getField(GUIGlobals.FILE_FIELD);
if (jabrefFiles != null) {
// path linked in jabref
for (String jabrefFile : parsePathNames(entry, jabrefFiles)) {
if (jabrefFile.endsWith(nodeFileName)) {
return entry;
}
}
}
}
}
catch (ResolveDuplicateEntryAbortedException e) {
if (ignores != null) {
if (nodeFileName != null) {
ignores.add(nodeFileName);
}
}
throw e;
}
BibtexEntry entry = database.getEntryByKey(baseName);
return entry;
}
public static ArrayList<String> parsePathNames(BibtexEntry entry, String path) {
ArrayList<String> fileNames = new ArrayList<String>();
ArrayList<String> paths = extractPaths(path);
if (path == null) {
LogUtils.warn("Could not extract path from: " + entry.getCiteKey());
return fileNames;
}
if (paths == null || paths.size() == 0) {
return fileNames;
}
for (String s : paths) {
try {
if (Compat.isWindowsOS()) {
fileNames.add(new File(s).getPath());
}
else {
// DOCEAR - maybe no escape removal -> could cause problems
// like in win os
fileNames.add(new File(removeEscapingCharacter(s)).getPath());
}
}
catch (Exception e) {
continue;
}
}
return fileNames;
}
public ArrayList<URI> parsePaths(DocearWorkspaceProject project, BibtexEntry entry, String pathInBibtexFile) {
ArrayList<URI> uris = new ArrayList<URI>();
ArrayList<String> paths = extractPaths(pathInBibtexFile);
for (String path : paths) {
if (path == null) {
LogUtils.warn("Could not extract path from: " + entry.getCiteKey());
continue;
}
path = removeEscapingCharacter(path);
if (isAbsolutePath(path) && (new File(path)).exists()) {
uris.add(new File(path).toURI());
}
else {
URI absUri = URIUtils.getAbsoluteURI(project.getBibtexDatabase());
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
URI pdfUri = absUri.resolve(UriBuilder.fromPath(path).build());
Thread.currentThread().setContextClassLoader(contextClassLoader);
File file = null;
try {
file = new File(pdfUri);
}
catch (IllegalArgumentException e) {
LogUtils.warn(e.getMessage() + " for: " + path);
}
if (file != null && file.exists()) {
uris.add(pdfUri);
}
}
}
return uris;
}
private static boolean isAbsolutePath(String path) {
return path.matches("^/.*") || path.matches("^[a-zA-Z]:.*");
}
private static String removeEscapingCharacter(String string) {
return string.replaceAll("([^\\\\]{1,1})[\\\\]{1}", "$1");
}
public static ArrayList<String> extractPaths(String fileField) {
ArrayList<String> paths = new ArrayList<String>();
if (fileField != null) {
FileListTableModel model = new FileListTableModel();
model.setContent(fileField);
for (int i = 0; i < model.getRowCount(); i++) {
paths.add(model.getEntry(i).getLink());
}
}
return paths;
}
public void generateBibtexEntry(BibtexEntry entry) {
BibtexDatabase database = ReferencesController.getController().getJabrefWrapper().getDatabase();
LabelPatternUtil.makeLabel(Globals.prefs.getKeyPattern(), database, entry);
}
public boolean isNodeDirty() {
return nodeDirty;
}
public void setNodeDirty(boolean nodeDirty) {
this.nodeDirty = nodeDirty;
}
}