Package wyfs.util

Source Code of wyfs.util.AbstractFolder

// Copyright (c) 2012, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package wyfs.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import wybs.lang.*;
import wyfs.lang.Content;
import wyfs.lang.Path;
import wyfs.lang.Path.Entry;
import wyfs.lang.Path.ID;

/**
* An abstract folder contains other folders, and path entries. As such, it
* cannot be considered a concrete entry which can be read and written in the
* normal manner. Rather, it provides access to entries. In a physical file
* system, a folder would correspond to a directory.
*
* @author David J. Pearce
*
*/
public abstract class AbstractFolder implements Path.Folder {
  protected final Path.ID id;
  private Path.Item[] contents;
  private int nentries;

  /**
   * Construct an Abstract Folder representing a given ID (taken relative to
   * the enclosing root).
   *
   * @param id
   */
  public AbstractFolder(Path.ID id) {
    this.id = id;
  }

  @Override
  public Path.ID id() {
    return id;
  }

  @Override
  public boolean contains(Path.Entry<?> e) throws IOException {
    updateContents();
    Path.ID eid = e.id();
    boolean contained;

    if(id == eid.parent()) {
      // The requested entry is contained in this folder. Therefore, we
      // need to search for it.
      contained = true;
    } else if(id == eid.subpath(0,id.size())) {
      // This folder is a parent of the requested entry. Therefore, we
      // need to looking for a matching folder entry. If we find one, then
      // we ask it for the requested entry.
      eid = eid.subpath(0,id.size()+1);
      contained = false;
    } else {
      return false;
    }

    int idx = binarySearch(contents,nentries,eid);
    if(idx >= 0) {
      // At this point, we've found a matching index for the given ID.
      // However, there maybe multiple matching IDs (e.g. with different
      // content types). Therefore, we need to check them all to see if
      // they match the requested entry.
      Path.Item item = contents[idx];
      do {
        if (item == e) {
          return true;
        } else if(!contained && item instanceof Path.Folder) {
          Path.Folder folder = (Path.Folder) item;
          return folder.contains(e);
        }
      } while (++idx < nentries
          && (item = contents[idx]).id().equals(eid));
    }

    // no dice
    return false;
  }

  @Override
  public boolean exists(ID id, Content.Type<?> ct) throws IOException{
    return get(id,ct) != null;
  }

  @Override
  public <T> Path.Entry<T> get(ID eid, Content.Type<T> ct) throws IOException{
    updateContents();

    ID tid = id.append(eid.get(0));

    int idx = binarySearch(contents,nentries,tid);
    if(idx >= 0) {
      // At this point, we've found a matching index for the given ID.
      // However, there maybe multiple matching IDs with different
      // content types. Therefore, we need to check them all to see if
      // they match the requested entry.
      Path.Item item = contents[idx];
      do {
        if(item instanceof Entry && eid.size() == 1) {
          // In this case, we're looking for and have found an exact
          // item.
          Entry entry = (Entry) item;
          if (entry.contentType() == ct) {
            return entry;
          }
        } else if (item instanceof Path.Folder && eid.size() > 1) {
          // In this case, the ID is indicates the item is not
          // contained in this folder.
          Path.Folder folder = (Path.Folder) item;
          return folder.get(eid.subpath(1,eid.size()), ct);
        }
      } while (++idx < nentries
          && (item = contents[idx]).id().equals(tid));
    }

    // no dice
    return null;
  }

  @Override
  public List<Entry<?>> getAll() throws IOException{
    ArrayList entries = new ArrayList();
    updateContents();

    // It would be nice to further optimise this loop. Basically, to avoid
    // creating so many ArrayList objects. However, it's tricky to get right
    // given Java's generic type system.

    for(int i=0;i!=nentries;++i) {
      Path.Item item = contents[i];
      if(item instanceof Entry) {
        Entry entry = (Entry) item;
        entries.add(entry);
      } else if (item instanceof Path.Folder) {
        Path.Folder folder = (Path.Folder) item;
        entries.addAll(folder.getAll());
      }
    }

    return entries;
  }

  @Override
  public <T> void getAll(Content.Filter<T> filter, List<Entry<T>> entries) throws IOException{
    updateContents();

    // It would be nice to further optimise this loop. The key issue is that,
    // at some point, we might know the filter could never match. In which
    // case, we want to stop the recursion early, rather than exploring a
    // potentially largel subtree.

    for(int i=0;i!=nentries;++i) {
      Path.Item item = contents[i];
      if(item instanceof Entry) {
        Entry entry = (Entry) item;
        if(filter.matches(entry.id(),entry.contentType())) {
          entries.add(entry);
        }
      } else if (item instanceof Path.Folder
          && filter.matchesSubpath(item.id())) {
        Path.Folder folder = (Path.Folder) item;
        folder.getAll(filter, entries);
      }
    }
  }

  @Override
  public <T> void getAll(Content.Filter<T> filter, Set<Path.ID> entries) throws IOException{
    updateContents();

    // It would be nice to further optimise this loop. The key issue is that,
    // at some point, we might know the filter could never match. In which
    // case, we want to stop the recursion early, rather than exploring a
    // potentially largel subtree.

    for(int i=0;i!=nentries;++i) {
      Path.Item item = contents[i];
      if (item instanceof Entry) {
        Entry entry = (Entry) item;
        if (filter.matches(entry.id(), entry.contentType())) {
          entries.add(entry.id());
        }
      } else if (item instanceof Path.Folder
          && filter.matchesSubpath(item.id())) {
        Path.Folder folder = (Path.Folder) item;
        folder.getAll(filter, entries);
      }
    }
  }

  @Override
  public void refresh() {
    contents = null;
  }

  @Override
  public void flush() throws IOException {
    if(contents != null) {
      for(int i=0;i!=nentries;++i) {
        contents[i].flush();
      }
    }
  }

  protected Path.Folder getFolder(String name) throws IOException {
    updateContents();

    ID tid = id.append(name);

    int idx = binarySearch(contents, nentries, tid);
    if (idx >= 0) {
      // At this point, we've found a matching index for the given ID.
      // However, there maybe multiple matching IDs with different
      // content types. Therefore, we need to check them all to see if
      // they match the requested entry.
      Path.Item item = contents[idx];
      do {
        if (item instanceof Path.Folder) {
          // In this case, the ID is indicates the item is not
          // contained in this folder.
          return (Path.Folder) item;
        }
      } while (++idx < nentries
          && (item = contents[idx]).id().equals(tid));
    }

    // no dice
    return null;
  }

  /**
   * Insert a newly created item into this folder. Observe we assume
   * <code>entry.id().parent() == id</code>.
   *
   * @param item
   */
  protected void insert(Path.Item item) throws IOException {
    if (item.id().parent() != id) {
      throw new IllegalArgumentException(
          "Cannot insert with incorrect Path.Item (" + item.id() + ") into AbstractFolder (" + id + ")");
    }
    updateContents();

    Path.ID id = item.id();
    int index = binarySearch(contents, nentries, id);

    if (index < 0) {
      index = -index - 1; // calculate insertion point
    } else {
      // indicates already an entry with a different content type
    }

    if ((nentries + 1) < contents.length) {
      System.arraycopy(contents, index, contents, index + 1, nentries
          - index);
    } else {
      Path.Item[] tmp = new Path.Item[(nentries + 1) * 2];
      System.arraycopy(contents, 0, tmp, 0, index);
      System.arraycopy(contents, index, tmp, index + 1, nentries - index);
      contents = tmp;
    }

    contents[index] = item;
    nentries++;
  }

  private final void updateContents() throws IOException{
    if(contents == null) {
      contents = contents();
      nentries = contents.length;
      Arrays.sort(contents,entryComparator);
    }
  }

  /**
   * Extract all entries from the given folder.
   */
  protected abstract Path.Item[] contents() throws IOException;

  private static final int binarySearch(final Path.Item[] children, int nchildren, final Path.ID key) {
    int low = 0;
        int high = nchildren-1;

        while (low <= high) {
            int mid = (low + high) >> 1;
            int c = children[mid].id().compareTo(key);
            if (c < 0) {
                low = mid + 1;
            } else if (c > 0) {
                high = mid - 1;
            } else {
              // found a batch, locate start point
              mid = mid - 1;
        while (mid >= 0 && children[mid].id().compareTo(key) == 0) {
          mid = mid - 1;
        }
        return mid + 1;
            }
        }
        return -(low + 1);
  }

  private static final Comparator<Path.Item> entryComparator = new Comparator<Path.Item>() {
    public int compare(Path.Item e1, Path.Item e2) {
      return e1.id().compareTo(e2.id());
    }
  };
}
TOP

Related Classes of wyfs.util.AbstractFolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.