FileDocCategorySizeDatePackage
ClassPathElement.javaAPI DocGlassfish v2 API11012Fri May 04 22:34:38 BST 2007com.sun.jdo.api.persistence.enhancer.util

ClassPathElement.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */


package com.sun.jdo.api.persistence.enhancer.util;

import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.io.File;
import java.io.IOException;
import java.io.FilenameFilter;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;

/**
 * Abstract base class for components of a class path.
 */

abstract class ClassPathElement {

  /* A link to the next element in the path */
  private ClassPathElement next;

  /* package local accessors */

  /**
   * If this class path element resolves the class, return a 
   * ClassFileSource for the class.
   */
  public abstract ClassFileSource sourceOf(String className);

  /**
   * Return an enumeration of all of the class files in the specified 
   * package in this class path element.
   *
   * @param packageName specifies the VM format package name 
   *    to which class files must belong.
   * @returns an Enumeration of the VM format class names which
   *    can be found.  The return value may be null if the class
   *    path element is not valid.
   */
  public abstract Enumeration classesInPackage(String packageName);

  /**
   * Check to see if this ClassPathElement is a directory matching
   * the directory passed in.
   */
  abstract boolean matches(File directory);

  /**
   * Return the next class path element in the chain 
   */
  ClassPathElement next() {
    return this.next;
  }

  /**
   * Set the the next class path element in the chain 
   */
  void setNext(ClassPathElement next) {
    this.next = next;
  }

  /**
   * Construct a class path element 
   */
  ClassPathElement() {
  }

  /**
   * Create an appropriate type of class path element based on the
   * path element name.
   */
  static ClassPathElement create(String elementSpec) {
    File element = new File(elementSpec);
    if (!element.isDirectory() &&
	looksLikeZipName(elementSpec))
      return new ZipFileClassPathElement(element);
    else
      return new DirectoryClassPathElement(element);
  }

  /**
   * Append a class path element to the chain
   */
  void append(ClassPathElement another) {
    ClassPathElement e = this;
    while (e.next() != null)
      e = e.next();
    e.next = another;
  }

  /**
   * Check whether the String "looks" like a zip file name.
   */
  static protected boolean looksLikeZipName(String fname) {
    return (fname.length() > 4 &&
	    (fname.regionMatches(true, fname.length() - 4, ".zip", 0, 4) ||//NOI18N
	     fname.regionMatches(true, fname.length() - 4, ".jar", 0, 4)));//NOI18N
  }

}

/**
 * DirectoryClassPathElement represents a component of a class path
 * which is believe to be a directory.
 */

class DirectoryClassPathElement extends ClassPathElement {

  private File directory;

  private boolean exists;

  /* package local accessors */

  /**
   * If this class path element resolves the class, return a 
   * ClassFileSource for the class.
   */
  public ClassFileSource sourceOf(String className) {
    File f = fileOf(className);
    if (f != null && f.exists()) {
      return new ClassFileSource(className, f);
    }
    return null;
  }

  public Enumeration classesInPackage(String packageName) {
    if (!exists)
      return null;

    return new DirectoryClassPackageEnumerator(directory, packageName);
  }

  boolean matches(File matchDirectory) {
    String dir = FilePath.canonicalize(directory);
    String matchDir = FilePath.canonicalize(matchDirectory);
    return FilePath.canonicalNamesEqual(dir, matchDir);
  }

  /**
   * Construct a class path element 
   */
  DirectoryClassPathElement(File dirSpec) {
    directory = dirSpec;
    checkValid();
  }

  /* private methods */

  /**
   * fileOf Return a File object which might reasonably contain the
   * specified class.  The contents may or may not be valid.
   */
  private File fileOf(String className) {
    if (exists && directory.isDirectory()) {
      StringBuffer newPath = new StringBuffer(directory.getPath());
      if (newPath.charAt(newPath.length() - 1) != File.separatorChar)
	newPath.append(File.separatorChar);
      newPath.append(ClassPath.fileNameOf(className));

      File f = new File(newPath.toString());
      if (f.isFile())
	return f;
    }

    return null;
  }

  /**
   * Is this class path element valid?  That is, does the directory
   * exist with the specified name?
   */
  private boolean isValid() {
    return exists;
  }

  private void checkValid() {
    exists = directory.isDirectory();
  }
}

/**
 * ZipFileClassPathElement represents a component of a class path
 * which is believe to be a zip file containing classes.
 */

class ZipFileClassPathElement extends ClassPathElement {

  private File zipFileElement;
  private ZipFile zipFile;

  /* package local accessors */

  /**
   * If this class path element resolves the class, return a 
   * ClassFileSource for the class.
   */
  public ClassFileSource sourceOf(String className) {
    if (zipFile != null) {
      ZipEntry entry =
	zipFile.getEntry(ClassPath.zipFileNameOf(className));
      if (entry != null) {
	return new ClassFileSource(className, zipFile);
      }
    }
    return null;
  }

  public Enumeration classesInPackage(String packageName) {
    if (zipFile == null)
      return null;

    return new ZipFileClassPackageEnumerator(zipFile, packageName);
  }

  boolean matches(File directory) {
    return false;
  }

  /**
   * Construct a zip file class path element 
   */
  ZipFileClassPathElement(File elementSpec) {
    zipFileElement = elementSpec;
    checkValid();
  }

  /* private methods */

  private void checkValid() {
    if (looksLikeZipName(zipFileElement.getPath()) &&
	zipFileElement.isFile()) {
      try {
	zipFile = ZipFileRegistry.openZipFile(zipFileElement);
      } catch (IOException e) {
	System.err.println("IO exception while reading " +
			   zipFileElement.getPath());
	zipFile = null;
      }
    }
  }
}


/**
 * An enumeration class which returns the names of the classes which
 * can be found relative to a particular directory.
 */

class DirectoryClassPackageEnumerator 
  implements Enumeration, FilenameFilter {

  private String[] matches;
  private int nextMatch = -1;
  String searchPackage;

  /**
   * Constructor 
   * @param directory The directory to be used as the root of the
   *   package structure.
   * @param packageName The name of the package to search (in VM form).
   */
  DirectoryClassPackageEnumerator(File directory, String packageName) {
    searchPackage = packageName;
    String packageDirName = directory.getPath() + File.separator + 
      packageName.replace('/', File.separatorChar);

    File packageDir = new File(packageDirName);
    if (packageDir.isDirectory()) {
      matches = packageDir.list(this);
      if (matches != null && matches.length > 0)
	nextMatch = 0;
    }
  }

  public boolean hasMoreElements() {
    return (nextMatch >= 0);
  }

  public Object nextElement() {
    if (!hasMoreElements())
      throw new NoSuchElementException();
    String next = matches[nextMatch++];
    if (nextMatch >= matches.length)
      nextMatch = -1;
    return ClassPath.classNameOf(searchPackage + "/" + next);//NOI18N
  }

  /**
   * Check whether the file name is valid.
   * Needed for FilenameFilter implementation.
   */

  public boolean accept(File dir, String name) {
    int nameLength = name.length();
    boolean isOk = (nameLength > 6 &&
		    name.regionMatches(true, nameLength - 6, ".class", 0, 6));//NOI18N
    return isOk;
  }

}

/**
 * An enumeration class which returns the names of the classes which
 * can be found within a zip file.
 */

class ZipFileClassPackageEnumerator implements Enumeration {
  Enumeration zipFileEntries;
  ZipEntry nextEntry;
  String packageName;

  ZipFileClassPackageEnumerator(ZipFile zipFile, String packageName) {
    zipFileEntries = zipFile.entries();
    this.packageName = packageName;
  }

  public boolean hasMoreElements() {
    while (nextEntry == null && zipFileEntries != null &&
	   zipFileEntries.hasMoreElements()) {
      ZipEntry ent = (ZipEntry) zipFileEntries.nextElement();
      String memName = ent.getName();
      int memNameLength = memName.length();
      int packageNameLength = packageName.length();

      /* Check that the package name is a prefix of the member name.
	 Note that we rely here on the fact that zip file have a separator
	 character identical to the VM package separator */

      if (memNameLength > packageNameLength + 1 &&
	  memName.regionMatches(false, 0, packageName, 
				0, packageNameLength) &&
	  memName.charAt(packageNameLength) == '/') {
	if (memName.indexOf('/', packageNameLength+1) == -1) {
	  boolean isOk =
	    (memNameLength > packageNameLength+7 &&
	     memName.regionMatches(true, memNameLength - 6, ".class", 0, 6));//NOI18N
	  if (isOk)
	    nextEntry = ent;
	}
      }
    }
    return nextEntry != null;
  }

  public Object nextElement() {
    if (!hasMoreElements())
      throw new NoSuchElementException();
    String className = nextEntry.getName();
    nextEntry = null;
    return ClassPath.classNameOf(className);
  }
}