blob: fd6d606dd10fb0a6c3498d2f1daa7a54abf204f5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.internal;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.ClassFileContainerVisitor;
import org.eclipse.pde.api.tools.internal.provisional.IClassFile;
import org.eclipse.pde.api.tools.internal.provisional.IClassFileContainer;
import org.eclipse.pde.api.tools.internal.util.Util;
/**
* A class file container rooted at a directory in the file system.
*
* @since 1.0.0
*/
public class DirectoryClassFileContainer implements IClassFileContainer {
/**
* Root directory of the class file container
*/
private File fRoot;
/**
* Origin of this class file container
*/
private String fOrigin;
/**
* Map of package names to associated directory (file)
*/
private Map fPackages;
/**
* Cache of package names
*/
private String[] fPackageNames;
/**
* Implementation of a class file in the local file system.
*
* @since 1.0.0
*/
class ClassFile extends AbstractClassFile implements Comparable {
/**
* Associated file
*/
private File fFile;
/**
* Qualified type name. Package is dot separated and types are $-separated.
*/
private String fTypeName;
/**
* Constructs a class file on the given file
* @param file file
* @param qualified type name
*/
public ClassFile(File file, String typeName) {
fFile = file;
fTypeName = typeName;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFile#getTypeName()
*/
public String getTypeName() {
return fTypeName;
}
/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object o) {
return fTypeName.compareTo(((ClassFile)o).fTypeName);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof ClassFile) {
return ((ClassFile) obj).fTypeName.equals(this.fTypeName);
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return this.fTypeName.hashCode();
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFile#getInputStream()
*/
public InputStream getInputStream() throws CoreException {
try {
return new FileInputStream(fFile);
} catch (FileNotFoundException e) {
abort("File not found", e); //$NON-NLS-1$
}
return null; // never reaches here
}
}
/**
* Constructs a class file container rooted at the specified path.
*
* @param location absolute path in the local file system
* @param origin id of the component that creates this class file container
*/
public DirectoryClassFileContainer(String location, String origin) {
this.fRoot = new File(location);
this.fOrigin = origin;
}
/**
* Throws a core exception with the given message and underlying exception,
* if any.
*
* @param message error message
* @param e underlying exception or <code>null</code>
* @throws CoreException
*/
private void abort(String message, Throwable e) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, e));
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFileContainer#accept(org.eclipse.pde.api.tools.model.component.ClassFileContainerVisitor)
*/
public void accept(ClassFileContainerVisitor visitor) throws CoreException {
init();
String[] packageNames = getPackageNames();
for (int i = 0; i < packageNames.length; i++) {
String pkg = packageNames[i];
if (visitor.visitPackage(pkg)) {
File dir = (File) fPackages.get(pkg);
File[] files = dir.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isFile() && file.getName().endsWith(Util.DOT_CLASS_SUFFIX);
}
});
if (files != null) {
List classFiles = new ArrayList();
for (int j = 0; j < files.length; j++) {
File file = files[j];
String name = file.getName();
String typeName = name.substring(0, name.length() - 6);
if (pkg.length() > 0) {
typeName = pkg + "." + typeName; //$NON-NLS-1$
}
classFiles.add(new ClassFile(file, typeName));
}
Collections.sort(classFiles);
Iterator cfIterator = classFiles.iterator();
while (cfIterator.hasNext()) {
IClassFile classFile = (IClassFile) cfIterator.next();
visitor.visit(pkg, classFile);
visitor.end(pkg, classFile);
}
}
}
visitor.endVisitPackage(pkg);
}
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFileContainer#close()
*/
public synchronized void close() throws CoreException {
fPackages = null;
fPackageNames = null;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFileContainer#findClassFile(java.lang.String)
*/
public IClassFile findClassFile(String qualifiedName) throws CoreException {
init();
int index = qualifiedName.lastIndexOf('.');
String cfName = qualifiedName;
String pkg = Util.DEFAULT_PACKAGE_NAME;
if (index > 0) {
pkg = qualifiedName.substring(0, index);
cfName = qualifiedName.substring(index + 1);
}
File dir = (File) fPackages.get(pkg);
if (dir != null) {
File file = new File(dir, cfName + Util.DOT_CLASS_SUFFIX);
if (file.exists()) {
return new ClassFile(file, qualifiedName);
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.IClassFileContainer#getPackageNames()
*/
public String[] getPackageNames() throws CoreException {
init();
if (fPackageNames == null) {
List names = new ArrayList(fPackages.keySet());
Collections.sort(names);
fPackageNames = (String[]) names.toArray(new String[names.size()]);
}
return fPackageNames;
}
/**
* Builds cache of package names to directories
*/
private synchronized void init() {
if (fPackages == null) {
fPackages = new HashMap();
processDirectory(Util.DEFAULT_PACKAGE_NAME, fRoot);
}
}
/**
* Traverses a directory to determine if it has class files and
* then visits sub-directories.
*
* @param packageName package name of directory being visited
* @param dir directory being visited
*/
private void processDirectory(String packageName, File dir) {
File[] files = dir.listFiles();
if (files != null) {
boolean hasClassFiles = false;
List dirs = new ArrayList();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
dirs.add(file);
} else if (!hasClassFiles) {
if (file.getName().endsWith(Util.DOT_CLASS_SUFFIX)) {
fPackages.put(packageName, dir);
hasClassFiles = true;
}
}
}
Iterator iterator = dirs.iterator();
while (iterator.hasNext()) {
File child = (File)iterator.next();
String nextName = null;
if (packageName.length() == 0) {
nextName = child.getName();
} else {
nextName = packageName + "." + child.getName(); //$NON-NLS-1$
}
processDirectory(nextName, child);
}
}
}
public IClassFile findClassFile(String qualifiedName, String id) throws CoreException {
return findClassFile(qualifiedName);
}
public String getOrigin() {
return this.fOrigin;
}
}