Skip to content

Commit

Permalink
Merge pull request #900 from JabRef/aux-dialog
Browse files Browse the repository at this point in the history
Rework Aux dialog and logic
  • Loading branch information
stefan-kolb committed Mar 5, 2016
2 parents b8f2590 + cee10d4 commit 96fdf88
Show file tree
Hide file tree
Showing 20 changed files with 496 additions and 494 deletions.
2 changes: 1 addition & 1 deletion src/main/java/net/sf/jabref/JabRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
import net.sf.jabref.model.database.BibDatabaseMode;
import net.sf.jabref.model.entry.BibEntry;
import net.sf.jabref.util.Util;
import net.sf.jabref.wizard.auximport.AuxCommandLine;
import net.sf.jabref.cli.AuxCommandLine;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Expand Down
29 changes: 29 additions & 0 deletions src/main/java/net/sf/jabref/cli/AuxCommandLine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.sf.jabref.cli;

import net.sf.jabref.logic.auxparser.AuxParser;
import net.sf.jabref.logic.auxparser.AuxParserResult;
import net.sf.jabref.model.database.BibDatabase;
import net.sf.jabref.logic.util.strings.StringUtil;

public class AuxCommandLine {
private final String auxFile;
private final BibDatabase database;

public AuxCommandLine(String auxFile, BibDatabase database) {
this.auxFile = StringUtil.getCorrectFileName(auxFile, "aux");
this.database = database;
}

public BibDatabase perform() {
BibDatabase subDatabase = null;

if (!auxFile.isEmpty() && (database != null)) {
AuxParser auxParser = new AuxParser(auxFile, database);
AuxParserResult result = auxParser.parse();
subDatabase = result.getGeneratedBibDatabase();
// print statistics
System.out.println(result.getInformation(true));
}
return subDatabase;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import net.sf.jabref.gui.util.PositionWindow;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.database.BibDatabaseMode;
import net.sf.jabref.wizard.auximport.gui.FromAuxDialog;
import net.sf.jabref.gui.auximport.FromAuxDialog;

import javax.swing.*;
import java.awt.event.ActionEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@
// modified : 18.04.2006 r.nagel
// insert a "short info" section

package net.sf.jabref.wizard.auximport.gui;
package net.sf.jabref.gui.auximport;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
Expand All @@ -61,13 +60,12 @@
import net.sf.jabref.Globals;
import net.sf.jabref.gui.BasePanel;
import net.sf.jabref.gui.keyboard.KeyBinding;
import net.sf.jabref.logic.auxparser.AuxParserResult;
import net.sf.jabref.model.database.BibDatabase;
import net.sf.jabref.model.entry.BibEntry;
import net.sf.jabref.gui.JabRefFrame;
import net.sf.jabref.gui.FileDialogs;
import net.sf.jabref.gui.maintable.MainTable;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.wizard.auximport.AuxFileParser;
import net.sf.jabref.logic.auxparser.AuxParser;

import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.DefaultFormBuilder;
Expand All @@ -91,20 +89,18 @@ public class FromAuxDialog extends JDialog {

private boolean generatePressed;

private final AuxFileParser auxParser;
private AuxParser auxParser;

private final JabRefFrame parentFrame;


public FromAuxDialog(JabRefFrame frame, String title, boolean modal,
JTabbedPane viewedDBs) {
JTabbedPane viewedDBs) {
super(frame, title, modal);

parentTabbedPane = viewedDBs;
parentFrame = frame;

auxParser = new AuxFileParser();

jbInit();
pack();
setSize(600, 500);
Expand Down Expand Up @@ -225,17 +221,19 @@ private void parseActionPerformed() {
String auxName = auxFileField.getText();

if ((auxName != null) && (refBase != null) && !auxName.isEmpty()) {
auxParser.clear();
List<String> list = auxParser.generateBibDatabase(auxName, refBase);
notFoundList.setListData(list.toArray(new String[list.size()]));
statusInfos.append(auxParser.getInformation(false));
auxParser = new AuxParser(auxName, refBase);
AuxParserResult result = auxParser.parse();
notFoundList.setListData(result.getUnresolvedKeys().toArray(new String[result.getUnresolvedKeys().size()]));
statusInfos.append(result.getInformation(false));

generateButton.setEnabled(true);
}

// the generated database contains no entries -> no active generate-button
if (auxParser.getGeneratedBibDatabase().isEmpty()) {
statusInfos.append("\n" + Localization.lang("empty database"));
// the generated database contains no entries -> no active generate-button
if (result.getGeneratedBibDatabase().isEmpty()) {
statusInfos.append("\n" + Localization.lang("empty database"));
generateButton.setEnabled(false);
}
} else {
generateButton.setEnabled(false);
}

Expand All @@ -247,7 +245,7 @@ public boolean generatePressed() {
}

public BibDatabase getGenerateDB() {
return auxParser.getGeneratedBibDatabase();
return auxParser.parse().getGeneratedBibDatabase();
}

/**
Expand Down
166 changes: 166 additions & 0 deletions src/main/java/net/sf/jabref/logic/auxparser/AuxParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package net.sf.jabref.logic.auxparser;

import net.sf.jabref.model.database.BibDatabase;
import net.sf.jabref.model.entry.BibEntry;
import net.sf.jabref.model.entry.IdGenerator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* LaTeX Aux to BibTeX Parser
* <p>
* Extracts a subset of BibTeX entries from a BibDatabase that are included in an aux file.
*/
public class AuxParser {
private static final Log LOGGER = LogFactory.getLog(AuxParser.class);

private static final Pattern CITE_PATTERN = Pattern.compile("\\\\(citation|abx@aux@cite)\\{(.+)\\}");
private static final Pattern INPUT_PATTERN = Pattern.compile("\\\\@input\\{(.+)\\}");

private final String auxFile;
private final BibDatabase masterDatabase;

/**
* Generates a database based on the given aux file and BibTeX database
*
* @param auxFile Path to the LaTeX aux file
* @param database BibTeX database
*/
public AuxParser(String auxFile, BibDatabase database) {
this.auxFile = auxFile;
masterDatabase = database;
}

/**
* Executes the parsing logic and returns a result containing all information and the generated BibDatabase.
*
* @return an AuxParserResult containing the generated BibDatabase and parsing statistics
*/
public AuxParserResult parse() {
return parseAuxFile();
}

/*
* Parses the aux file and extracts all bib keys.
* Also supports nested aux files (latex \\include).
*
* There exists no specification of the aux file.
* Every package, class or document can write to the aux file.
* The aux file consists of LaTeX macros and is read at the \begin{document} and again at the \end{document}.
*
* BibTeX citation: \citation{x,y,z}
* Biblatex citation: \abx@aux@cite{x,y,z}
* Nested aux files: \@input{x}
*/
private AuxParserResult parseAuxFile() {
AuxParserResult result = new AuxParserResult(masterDatabase);

// nested aux files
List<String> fileList = new ArrayList<>(1);
fileList.add(auxFile);

int fileIndex = 0;

while (fileIndex < fileList.size()) {
String file = fileList.get(fileIndex);

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;

while ((line = br.readLine()) != null) {
Matcher citeMatch = CITE_PATTERN.matcher(line);

while (citeMatch.find()) {
String keyString = citeMatch.group(2);
String[] keys = keyString.split(",");

for (String key : keys) {
result.uniqueKeys.add(key.trim());
}
}

Matcher inputMatch = INPUT_PATTERN.matcher(line);

while (inputMatch.find()) {
String inputString = inputMatch.group(1);

String inputFile = inputString;
Path rootPath = new File(auxFile).toPath().getParent();
if (rootPath != null) {
inputFile = rootPath.resolve(inputString).toString();
}

if (!fileList.contains(inputFile)) {
fileList.add(inputFile);
result.nestedAuxCount++;
}
}
}
} catch (FileNotFoundException e) {
LOGGER.info("Cannot locate input file", e);
} catch (IOException e) {
LOGGER.warn("Problem opening file", e);
}

fileIndex++;
}
resolveTags(result);

return result;
}

/*
* Try to find an equivalent BibTeX entry inside the reference database for all keys inside the aux file.
*/
private void resolveTags(AuxParserResult result) {
for (String key : result.uniqueKeys) {
BibEntry entry = masterDatabase.getEntryByKey(key);

if (entry == null) {
result.unresolvedKeys.add(key);
} else {
insertEntry(entry, result);
resolveCrossReferences(entry, result);
}
}

// Copy database definitions
if (result.auxDatabase.getEntryCount() > 0) {
result.auxDatabase.copyPreamble(masterDatabase);
result.auxDatabase.copyStrings(masterDatabase);
}
}

/*
* Resolves and adds CrossRef entries
*/
private void resolveCrossReferences(BibEntry entry, AuxParserResult result) {
entry.getFieldOptional("crossref").ifPresent(crossref -> {
if (!result.uniqueKeys.contains(crossref)) {
BibEntry refEntry = masterDatabase.getEntryByKey(crossref);

if (refEntry == null) {
result.unresolvedKeys.add(crossref);
} else {
insertEntry(refEntry, result);
result.crossRefEntriesCount++;
}
}
});
}

/*
* Insert a clone of the given entry. The clone is given a new unique ID.
*/
private void insertEntry(BibEntry entry, AuxParserResult result) {
BibEntry clonedEntry = (BibEntry) entry.clone();
clonedEntry.setId(IdGenerator.next());
result.auxDatabase.insertEntry(clonedEntry);
}
}
79 changes: 79 additions & 0 deletions src/main/java/net/sf/jabref/logic/auxparser/AuxParserResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package net.sf.jabref.logic.auxparser;

import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.database.BibDatabase;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class AuxParserResult {
public final BibDatabase masterDatabase;
public final Set<String> uniqueKeys = new HashSet<>();
public final List<String> unresolvedKeys = new ArrayList<>();

public BibDatabase auxDatabase = new BibDatabase();
public int nestedAuxCount;
public int crossRefEntriesCount;

public AuxParserResult(BibDatabase masterDatabase) {
this.masterDatabase = masterDatabase;
}

public BibDatabase getGeneratedBibDatabase() {
return auxDatabase;
}

public List<String> getUnresolvedKeys() {
return unresolvedKeys;
}

public int getFoundKeysInAux() {
return uniqueKeys.size();
}

public int getResolvedKeysCount() {
return auxDatabase.getEntryCount() - crossRefEntriesCount;
}

public int getUnresolvedKeysCount() {
return unresolvedKeys.size();
}

/**
* Query the number of extra entries pulled in due to crossrefs from other entries.
*
* @return The number of additional entries pulled in due to crossref
*/
public int getCrossRefEntriesCount() {
return crossRefEntriesCount;
}

/**
* Prints parsing statistics
*
* @param includeMissingEntries
* @return
*/
public String getInformation(boolean includeMissingEntries) {
StringBuilder result = new StringBuilder();

result.append(Localization.lang("keys_in_database")).append(' ').append(masterDatabase.getEntryCount()).append('\n')
.append(Localization.lang("found_in_aux_file")).append(' ').append(getFoundKeysInAux()).append('\n')
.append(Localization.lang("resolved")).append(' ').append(getResolvedKeysCount()).append('\n')
.append(Localization.lang("not_found")).append(' ').append(getUnresolvedKeysCount()).append('\n')
.append(Localization.lang("crossreferenced entries included")).append(' ')
.append(getCrossRefEntriesCount()).append('\n');

if (includeMissingEntries && (getUnresolvedKeysCount() > 0)) {
for (String entry : unresolvedKeys) {
result.append(entry).append('\n');
}
}
if (nestedAuxCount > 0) {
result.append(Localization.lang("nested_aux_files")).append(' ').append(nestedAuxCount);
}
return result.toString();
}
}
Loading

0 comments on commit 96fdf88

Please sign in to comment.