-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #900 from JabRef/aux-dialog
Rework Aux dialog and logic
- Loading branch information
Showing
20 changed files
with
496 additions
and
494 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 166 additions & 0 deletions
166
src/main/java/net/sf/jabref/logic/auxparser/AuxParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
79
src/main/java/net/sf/jabref/logic/auxparser/AuxParserResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
Oops, something went wrong.