/* * $Id$ * * Copyright (c) 2004-2005 by the TeXlapse Team. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package net.sourceforge.texlipse.model; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Container for referencing data (BibTeX and labels.) Holds the reference * lists of each file as well as a (case insensitive) sorted array of all references, so that * not all files need to be reparsed when the data changes. * * @author Oskar Ojala * @author Boris von Loesch */ public class ReferenceContainer { private Map<String, List<ReferenceEntry>> referenceHash; private List<ReferenceEntry> sortedReferences; private int size; /** * Creates a new reference container and initializes its datastructures. */ public ReferenceContainer() { referenceHash = new HashMap<String, List<ReferenceEntry>>(4); sortedReferences = null; size = 0; } /** * Adds a reference source <code>refs</code> * associated with <code>key</code> into this object. Does not * update the sorted array of references. * * @param key The key associated with these references in the internal set * @param refs The references to store */ public void addRefSource(String key, List<ReferenceEntry> refs) { if (key != null && key.endsWith(".aux")) { // Avoid duplicates Iterator<ReferenceEntry> it = refs.iterator(); while (it.hasNext()) { String x = it.next().key; if (binTest(x)) { it.remove(); } } } // Add filename to all references for (ReferenceEntry r : refs) { r.fileName = key; } size += refs.size(); List<ReferenceEntry> al = referenceHash.put(key, refs); if (al != null) size -= al.size(); } /** * Updates the contents of this object if the given key exists * in the internal set. Updating includes replacing the old * data with the new and re-creating the sorted reference array. * * If the key doesn't exist, then nothing is done. Thus, this * method is suitable for use by the BibTeX editor to update * the BibTeX references in case these are used by the LaTeX editor. * * @param key The key with which to associate the reference source * @param refs The reference source * @return True if the internal set was changed */ public boolean updateRefSource(String key, List<ReferenceEntry> refs) { if (referenceHash.containsKey(key)) { this.addRefSource(key, refs); this.organize(); return true; } return false; } /** * Organizes the data from the reference hashes to the sorted array * <code>sortedReferences</code>. */ public void organize() { if (referenceHash.size() == 0) { sortedReferences = new ArrayList<ReferenceEntry>(0); return; } List<ReferenceEntry> allRefs = new ArrayList<ReferenceEntry>(size); if (referenceHash.size() > 1) { for (Iterator<List<ReferenceEntry>> iter = referenceHash.values().iterator(); iter.hasNext();) { List<ReferenceEntry> refList = iter.next(); allRefs.addAll(refList); } } else if (referenceHash.size() == 1) { Iterator<List<ReferenceEntry>> iter = referenceHash.values().iterator(); allRefs = iter.next(); } sortedReferences = allRefs; //Sort collections case insensitive Collections.sort(sortedReferences, new Comparator<ReferenceEntry>() { public int compare(ReferenceEntry o1, ReferenceEntry o2) { return o1.getkey(true).compareTo(o2.getkey(true).toLowerCase()); } }); } /** * Compares the set contained to the given keys in order to determine * whether the new keyset has changed or not. If it has, the names * of the new keys are returned as a list. If the internal set contains * keys which aren't in the given keyset, then the these kesy and their * datastructures are removed from the internal set. * * The naming of this method is due to the fact that it's really useful for * BibTeX files (ie. keys), but not for labels, as the BibTeX files are * all defined in one command and thus fetched simultaneously, whereas * labels are built incrementally. * * @param newBibs An array containing the new keys to store into the set * @return An empty list if there were no new keys, otherwise a list containing * the names of the new keys */ public List<String> updateBibHash(String[] newBibs) { List<String> toParse = new LinkedList<String>(); Map<String, List<ReferenceEntry>> newHash = new HashMap<String, List<ReferenceEntry>>(newBibs.length); int newSize = 0; for (String bib : newBibs) { List<ReferenceEntry> al = referenceHash.get(bib); if (al != null) { newHash.put(bib, al); newSize += al.size(); } else { toParse.add(bib); } } referenceHash = newHash; size = newSize; return toParse; } /** * Checks whether this container is fresh: it is up to date * if it contains exactly the same keys (or in practice BibTeX-files) * as those given as arguments. * * @param newBibs Array of the new keys * @return <code>true</code> if this container is up to date, false otherwise */ public boolean checkFreshness(String[] newBibs) { if (newBibs.length != referenceHash.size()) return false; for (String bib : newBibs) { if (!referenceHash.containsKey(bib)) return false; } return true; } /** * Tests (using binary search) if the given key exists in this container. * * @param key The key to look for * @return True if <code>key</code> was found, false if it was not found */ public boolean binTest(String key) { if (sortedReferences == null || sortedReferences.size() == 0) return false; int nr = PartialRetriever.getEntry(key, sortedReferences, true); return (nr >= 0); } /** * Takes a list of errors (<code>DocumentReference</code>) and * checks which of them are "false alarms" (ie. they exist in this container) * * This is used because the reference declarations might not be available * when the reference occurs in the document, so after updating and * organizing this container, this method should be used to find out * whit references are actually resolved. * * @param errors A list of errors in the form of <code>DocumentReference</code> */ public void removeFalseEntries(List<DocumentReference> errors) { for (Iterator<DocumentReference> iter = errors.iterator(); iter.hasNext();) { DocumentReference docRef = iter.next(); if (binTest(docRef.getKey())) { iter.remove(); } } } /** * Returns all the references in this container alphabetically sorted. * * @return Returns the sortedReferences. */ public List<ReferenceEntry> getSortedReferences() { return sortedReferences; } }