/* * Created on Mar 29, 2003 * * @author henkel@cs.colorado.edu * */ package bibtex.expansions; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import bibtex.dom.BibtexAbstractEntry; import bibtex.dom.BibtexAbstractValue; import bibtex.dom.BibtexConcatenatedValue; import bibtex.dom.BibtexEntry; import bibtex.dom.BibtexFile; import bibtex.dom.BibtexMacroDefinition; import bibtex.dom.BibtexMacroReference; import bibtex.dom.BibtexPreamble; import bibtex.dom.BibtexStandardMacros; import bibtex.dom.BibtexString; import bibtex.dom.BibtexToplevelComment; /** * This expander expands macro references into strings - have a look at the * options that can be given in the constructor. * * @author henkel */ public final class MacroReferenceExpander extends AbstractExpander implements Expander { /** * * This is just a convenience / backward compatibility constructor. It is * equivalent to calling MacroReferenceExpander(expandStandardMacros, * expandMonthAbbreviations, removeMacros, true); */ public MacroReferenceExpander( boolean expandStandardMacros, boolean expandMonthAbbreviations, boolean removeMacros) { this(expandStandardMacros, expandMonthAbbreviations, removeMacros, true); } /** * @param expandStandardMacros * Expand all standard macros as defined in the bibtex file * plain.bst * @param expandMonthAbbreviations * Expand all month abbreviations. This parameter has precedence * over the first one (note that the month abbreviations are a * subset of the standard macros). * @param removeMacros * Remove all macros from the bibtex model. * @param throwAllExpansionExceptions * Setting this to true means that all exceptions will be thrown * immediately. Otherwise, the expander will skip over things it * can't expand and you can use getExceptions to retrieve the * exceptions later */ public MacroReferenceExpander( boolean expandStandardMacros, boolean expandMonthAbbreviations, boolean removeMacros, boolean throwAllExpansionExceptions) { super(throwAllExpansionExceptions); this.expandMonthAbbreviations = expandMonthAbbreviations; this.expandStandardMacros = expandStandardMacros; this.removeMacros = removeMacros; } private final boolean expandStandardMacros; private final boolean expandMonthAbbreviations; private final boolean removeMacros; /** * This method walks over all entries in a BibtexFile and expands macro * references. Thus, after the execution of this function, all fields * contain BibtexString entries. Exceptions: 1) the crossref fields 2) * 3-letter month abbreviations and standard macros, if specified in the * constructor (MacroReferenceExpander). * * If you use the flag throwAllExpansionExceptions set to false, you can * retrieve all the exceptions using getExceptions() * * @param bibtexFile */ public void expand(BibtexFile bibtexFile) throws ExpansionException { HashMap stringKey2StringValue = new HashMap(); for (Iterator entryIt = bibtexFile.getEntries().iterator(); entryIt.hasNext();) { BibtexAbstractEntry abstractEntry = (BibtexAbstractEntry) entryIt.next(); if (abstractEntry instanceof BibtexMacroDefinition) { BibtexMacroDefinition bibtexStringDefinition = (BibtexMacroDefinition) abstractEntry; BibtexAbstractValue simplifiedValue = simplify(bibtexFile, bibtexStringDefinition.getValue(), stringKey2StringValue); bibtexStringDefinition.setValue(simplifiedValue); if (removeMacros) { bibtexFile.removeEntry(bibtexStringDefinition); }; stringKey2StringValue.put(bibtexStringDefinition.getKey().toLowerCase(), simplifiedValue); } else if (abstractEntry instanceof BibtexPreamble) { BibtexPreamble preamble = (BibtexPreamble) abstractEntry; preamble.setContent(simplify(bibtexFile, preamble.getContent(), stringKey2StringValue)); } else if (abstractEntry instanceof BibtexEntry) { BibtexEntry entry = (BibtexEntry) abstractEntry; for (Iterator fieldIt = entry.getFields().entrySet().iterator(); fieldIt.hasNext();) { Map.Entry field = (Map.Entry) fieldIt.next(); if (!(field.getValue() instanceof BibtexString)) { entry.setField( (String) field.getKey(), simplify(bibtexFile, (BibtexAbstractValue) field.getValue(), stringKey2StringValue)); } } } else if (abstractEntry instanceof BibtexToplevelComment) { // don't do anything here ... } else { throwExpansionException( "MacroReferenceExpander.expand(): I don't support \"" + abstractEntry.getClass().getName() + "\". Use the force, read the source!"); } } finishExpansion(); } private BibtexAbstractValue simplify( BibtexFile factory, BibtexAbstractValue compositeValue, HashMap stringKey2StringValue) throws ExpansionException { if (compositeValue instanceof BibtexString) return (BibtexString) compositeValue; if (compositeValue instanceof BibtexMacroReference) { BibtexMacroReference reference = (BibtexMacroReference) compositeValue; String key = reference.getKey(); BibtexString simplifiedValue = (BibtexString) stringKey2StringValue.get(key); if (simplifiedValue == null) { if (!this.expandMonthAbbreviations && BibtexStandardMacros.isMonthAbbreviation(key)) return reference; if (!this.expandStandardMacros && BibtexStandardMacros.isStandardMacro(key)) return reference; if (BibtexStandardMacros.isStandardMacro(key)) { return factory.makeString(BibtexStandardMacros.resolveStandardMacro(key)); } else { throwExpansionException( "Invalid macro reference (target does not exist): \"" + reference.getKey() + "\""); return factory.makeString(""); // if target does not exist: // resolve to empty string. } } return simplifiedValue; } if (compositeValue instanceof BibtexConcatenatedValue) { BibtexConcatenatedValue concatenatedValue = (BibtexConcatenatedValue) compositeValue; BibtexAbstractValue left = simplify(factory, concatenatedValue.getLeft(), stringKey2StringValue); BibtexAbstractValue right = simplify(factory, concatenatedValue.getRight(), stringKey2StringValue); if (left instanceof BibtexString && right instanceof BibtexString) return factory.makeString( ((BibtexString) left).getContent() + ((BibtexString) right).getContent()); else return factory.makeConcatenatedValue(left, right); } throwExpansionException( "MacroReferenceExpander.simplify(): I don't support \"" + compositeValue.getClass().getName() + "\". Use the force, read the source!"); return factory.makeString(""); // so we don't know what to do, let's // use the empty string } }