/**
  * Clears away any URNs for files that do not exist anymore.
  * @param shouldClearURNSetMap true if you want to clear TIME_TO_URNSET_MAP
  * too
  */
 private void pruneTimes(boolean shouldClearURNSetMap) {
     // if i'm using FM, always grab that lock first and then me.  be quick
     // about it though :)
     synchronized (RouterService.getFileManager()) {
         synchronized (this) {
             Iterator iter = URN_TO_TIME_MAP.entrySet().iterator();
             while (iter.hasNext()) {
                 Map.Entry currEntry = (Map.Entry) iter.next();
                 if(!(currEntry.getKey() instanceof URN) ||
                    !(currEntry.getValue() instanceof Long)) {
                     iter.remove();
                     dirty = true;
                     continue;
                 }
                 URN currURN = (URN) currEntry.getKey();
                 Long cTime = (Long) currEntry.getValue();
                 
                 // check to see if file still exists
                 // NOTE: technically a URN can map to multiple FDs, but I only want
                 // to know about one.  getFileDescForUrn prefers FDs over iFDs.
                 FileDesc fd = RouterService.getFileManager().getFileDescForUrn(currURN);
                 if ((fd == null) || (fd.getFile() == null) || !fd.getFile().exists()) {
                     dirty = true;
                     iter.remove();
                     if (shouldClearURNSetMap)
                         removeURNFromURNSet(currURN, cTime);
                 }
             }
         }
     }
 }
    /**
     * Returns an List of URNs, from 'youngest' to 'oldest'.
     * @param max the maximum number of URNs you want returned.  if you
     * want all, give Integer.MAX_VALUE.
     * @param request in case the query has meta-flags, you can give it to
     * me. null is fine though.
     * @return a List ordered by younger URNs.
     */
    public List getFiles(final QueryRequest request, final int max)
        throws IllegalArgumentException {
        // if i'm using FM, always grab that lock first and then me.  be quick
        // about it though :)
        synchronized (RouterService.getFileManager()) {
        synchronized (this) {
        if (max < 1) throw new IllegalArgumentException("bad max = " + max);
        List urnList = new ArrayList();
        Iterator iter = TIME_TO_URNSET_MAP.entrySet().iterator();
        final MediaType.Aggregator filter = 
            (request == null ? null : MediaType.getAggregator(request));

        // may be non-null at loop end
        List toRemove = null;

        // we bank on the fact that the TIME_TO_URNSET_MAP iterator returns the 
        // entries in descending order....
        while (iter.hasNext() && (urnList.size() < max)) {
            Map.Entry currEntry = (Map.Entry) iter.next();
            Set urns = (Set) currEntry.getValue();

            // only put as many as desired, and possibly filter results based
            // on what the query desires
            Iterator innerIter = urns.iterator();
            while ((urnList.size() < max) && innerIter.hasNext()) {
                URN currURN = (URN) innerIter.next();
                FileDesc fd =
                    RouterService.getFileManager().getFileDescForUrn(currURN);
                // unfortunately fds can turn into ifds so ignore
                if ((fd == null) || (fd instanceof IncompleteFileDesc)) {
                    if (toRemove == null) toRemove = new ArrayList();
                    toRemove.add(currURN);
                    continue;
                }

                if (filter == null) urnList.add(currURN);
                else if (filter.allow(fd.getFileName())) urnList.add(currURN);
            }
        }

        // clear any ifd's or unshared files that may have snuck into structures
        if (toRemove != null) {
            Iterator removees = toRemove.iterator();
            while (removees.hasNext()) {
                URN currURN = (URN) removees.next();
                removeTime(currURN);
            }
        }

        return urnList;
        }
        }
    }
    /**
     * Constructs the TIME_TO_URNSET_MAP, which is based off the entries in the
     * URN_TO_TIME_MAP.
     * IMPORTANT NOTE: currently this method is not synchronized, and does not
     * need to be since it is only called from the constructor (which auto-
     * magically disallows concurrent acess to the instance.  If this method
     * is ever made public, called from multiple entrypoints, etc.,
     * synchronization may be needed.
     */
    private void constructURNMap() {
        Set entries = URN_TO_TIME_MAP.entrySet();
        Iterator iter = entries.iterator();
        while (iter.hasNext()) {
            // for each entry, get the creation time and the urn....
            Map.Entry currEntry = (Map.Entry) iter.next();
            Long cTime = (Long) currEntry.getValue();
            URN urn = (URN) currEntry.getKey();

            // don't ever add IFDs
            if (RouterService.getFileManager().getFileDescForUrn(urn)
                instanceof IncompleteFileDesc) continue;

            // put the urn in a set of urns that have that creation time....
            Set urnSet = (Set) TIME_TO_URNSET_MAP.get(cTime);
            if (urnSet == null) {
                urnSet = new HashSet();
                // populate the reverse mapping
                TIME_TO_URNSET_MAP.put(cTime, urnSet);
            }
            urnSet.add(urn);

        }
    }