/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.model.internal;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.imp.core.ErrorHandler;
import org.eclipse.imp.model.IPathEntry;
import org.eclipse.imp.model.ISourceEntity;
import org.eclipse.imp.model.ISourceFolder;
import org.eclipse.imp.model.ISourceProject;
import org.eclipse.imp.model.IWorkspaceModel;
import org.eclipse.imp.model.ModelFactory;
import org.eclipse.imp.model.IPathEntry.PathEntryType;
import org.eclipse.imp.model.ModelFactory.ModelException;
import org.eclipse.imp.runtime.RuntimePlugin;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class SourceProject implements ISourceProject {
private static final String CONFIG_FILE_NAME= ".projectConfig";
private final IProject fProject;
private final List<IPathEntry> fBuildPath= new ArrayList<IPathEntry>();
public SourceProject(IProject project) {
fProject= project;
if (!readMetaData()) {
// initializeFromJavaProject();
}
}
public void setBuildPath(List<IPathEntry> buildPath) {
fBuildPath.clear();
fBuildPath.addAll(buildPath);
}
public String getName() {
return fProject.getName();
}
private void saveMetaData(IProgressMonitor monitor) {
IFile file= fProject.getFile(CONFIG_FILE_NAME);
String metaData= createProjectMetaData();
try {
InputStream is= new ByteArrayInputStream(metaData.getBytes(file.getCharset()));
file.setContents(is, true, true, monitor);
} catch (CoreException e) {
ErrorHandler.reportError(e.getMessage(), e);
} catch (UnsupportedEncodingException e) {
ErrorHandler.reportError(e.getMessage(), e);
}
}
private String createProjectMetaData() {
StringBuffer buff= new StringBuffer();
buff.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
buff.append("<project>\n");
buff.append(" <searchPath>\n");
for(IPathEntry path: fBuildPath) {
buff.append(" <pathEntry type=\"" + path.getEntryType() + "\"\n");
buff.append(" loc=\"" + path.getPath().toPortableString() + "\"/>\n");
}
buff.append(" </searchPath>\n");
buff.append("</project>\n");
return buff.toString();
}
private boolean readMetaData() {
IFile file= fProject.getFile(CONFIG_FILE_NAME);
if (file.exists()) {
try {
InputStream contents= file.getContents();
DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
DocumentBuilder builder= factory.newDocumentBuilder();
Document document= builder.parse(contents);
NodeList children= document.getChildNodes();
Node project= children.item(0);
// String projLabel= project.getNodeName();
NodeList configItems= project.getChildNodes();
for(int i= 0; i < configItems.getLength(); i++) {
Node configItem= configItems.item(i);
String itemName= configItem.getNodeName();
if (itemName.equals("searchPath")) {
NodeList pathItems= configItem.getChildNodes();
for(int p= 0; p < pathItems.getLength(); p++) {
Node pathItem= pathItems.item(p);
String nodeName= pathItem.getNodeName();
if (nodeName.equals("#text"))
continue;
if (!nodeName.equals("pathEntry")) {
ErrorHandler.reportError("Unexpected child " + nodeName + " of <searchPath> in " + fProject.getFullPath().toPortableString()
+ "/" + CONFIG_FILE_NAME);
continue;
}
NamedNodeMap attribs= pathItem.getAttributes();
Node typeNode= attribs.getNamedItem("type");
Node locNode= attribs.getNamedItem("loc");
if (typeNode == null || locNode == null) {
ErrorHandler.reportError("<pathEntry> missing type or location in " + fProject.getFullPath().toPortableString() + "/"
+ CONFIG_FILE_NAME);
continue;
}
String pathType= typeNode.getNodeValue();
String pathLoc= locNode.getNodeValue();
IPathEntry entry= new PathEntry(PathEntryType.valueOf(pathType), new Path(pathLoc));
fBuildPath.add(entry);
}
} else if (itemName.equals("#text")) {
// skip over these; should be empty anyway
} else
ErrorHandler.reportError("Unrecognized project configuration item: " + itemName);
}
return true;
} catch (CoreException e) {
ErrorHandler.reportError(e.getMessage(), e);
} catch (ParserConfigurationException e) {
ErrorHandler.reportError(e.getMessage(), e);
} catch (SAXException e) {
ErrorHandler.reportError(e.getMessage(), e);
} catch (IOException e) {
ErrorHandler.reportError(e.getMessage(), e);
}
}
return false;
}
public IPath resolvePath(IPath path) {
List<IPathEntry> buildPath= getBuildPath();
IProject rawProject= fProject;
if (path.isAbsolute()) {
return path;
}
IFile projRelFile= rawProject.getFile(path);
if (projRelFile.exists())
return rawProject.getFullPath().append(path);
for(IPathEntry pathEntry: buildPath) {
IPath entryPath= pathEntry.getPath();
IPathEntry.PathEntryType type= pathEntry.getEntryType();
if (type == IPathEntry.PathEntryType.SOURCE_FOLDER) {
if (!entryPath.isAbsolute()) {
IPath filePath= entryPath.removeFirstSegments(1).append(path);
IFile file= rawProject.getFile(filePath);
if (file.exists()) {
return filePath;
}
} else {
// This is a pseudo source folder, not really contained within the project.
// The path points outside the workspace, and an IFile can't live outside the
// workspace. So use an ordinary Java File to determine whether this path is
// resolved.
IPath filePath= entryPath.append(path);
File f= new File(filePath.toOSString());
if (f.exists())
return filePath;
}
} else if (type == IPathEntry.PathEntryType.PROJECT) {
try {
IProject defProject= ResourcesPlugin.getWorkspace().getRoot().getProject(entryPath.toPortableString());
ISourceProject defSrcProject= ModelFactory.open(defProject);
IPath defPath= scanSourceFoldersFor(defSrcProject, path);
if (defPath != null)
return defPath;
} catch (ModelException e) {
ErrorHandler.reportError(e.getMessage());
}
} else if (type == IPathEntry.PathEntryType.ARCHIVE) {
// ???
}
}
return null;
}
private IPath scanSourceFoldersFor(ISourceProject srcProject, IPath path) {
List<IPathEntry> buildPath= srcProject.getBuildPath();
IProject rawProject= srcProject.getRawProject();
for(IPathEntry pathEntry: buildPath) {
IPath entryPath= pathEntry.getPath();
IPathEntry.PathEntryType type= pathEntry.getEntryType();
if (type == IPathEntry.PathEntryType.SOURCE_FOLDER) {
entryPath= entryPath.removeFirstSegments(1); // make project-relative
IPath filePath= entryPath.append(path);
IFile f= rawProject.getFile(filePath);
if (f.exists()) {
return filePath;
}
}
}
return null;
}
public List<IPathEntry> getBuildPath() {
return fBuildPath;
}
public IProject getRawProject() {
return fProject;
}
public IResource getResource() {
return fProject;
}
public ISourceFolder[] getSourceRoots() {
// TODO Bogus: considers ALL folders as source roots; need to read project config
final Set<ISourceFolder> result= new HashSet<ISourceFolder>();
Set<IResource> children= ResourceUtility.getImmediateChildren(fProject);
for(IResource child: children) {
if (child instanceof IFolder) {
try {
result.add(ModelFactory.open((IFolder) child));
} catch (ModelException e) {
RuntimePlugin.getInstance().logException(e.getMessage(), e);
}
}
}
return result.toArray(new ISourceFolder[result.size()]);
}
public ISourceEntity getParent() {
return ModelFactory.getModelRoot();
}
public ISourceEntity getAncestor(Class ofType) {
if (ofType == ISourceProject.class) {
return this;
} else if (ofType == IWorkspaceModel.class) {
return ModelFactory.getModelRoot();
}
return null;
}
public ISourceEntity[] getChildren() {
final Set<ISourceEntity> result= new HashSet<ISourceEntity>();
Set<IResource> children= ResourceUtility.getImmediateChildren(fProject);
for(IResource child: children) {
try {
result.add(ModelFactory.open(child));
} catch (ModelException e) {
RuntimePlugin.getInstance().logException(e.getMessage(), e);
}
}
return result.toArray(new ISourceEntity[result.size()]);
}
// Presumably this will get called by the "New Project" wizard and the Project Properties pages
public void commit(IProgressMonitor monitor) {
saveMetaData(monitor);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SourceProject))
return false;
SourceProject other= (SourceProject) obj;
return fProject.equals(other.fProject);
}
@Override
public int hashCode() {
int result= 6607;
result= result * 7451 + fProject.hashCode();
return result;
}
public String toString() {
return "<project: " + fProject.getName() + ">";
}
}