/*******************************************************************************
* Copyright © 2010, 2013 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.edt.ide.core.internal.model;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
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.edt.compiler.internal.core.utils.CharOperation;
import org.eclipse.edt.compiler.tools.EGL2IR;
import org.eclipse.edt.ide.core.internal.model.index.IDocument;
import org.eclipse.edt.ide.core.model.EGLModelException;
import org.eclipse.edt.ide.core.model.IBuffer;
import org.eclipse.edt.ide.core.model.IBufferFactory;
import org.eclipse.edt.ide.core.model.IClassFile;
import org.eclipse.edt.ide.core.model.IEGLElement;
import org.eclipse.edt.ide.core.model.IEGLModelStatusConstants;
import org.eclipse.edt.ide.core.model.IEGLPathEntry;
import org.eclipse.edt.ide.core.model.IEGLProject;
import org.eclipse.edt.ide.core.model.IPackageDeclaration;
import org.eclipse.edt.ide.core.model.IPackageFragment;
import org.eclipse.edt.ide.core.model.IPart;
import org.eclipse.edt.ide.core.model.IProblemRequestor;
import org.eclipse.edt.ide.core.model.ISourceRange;
public class ClassFile extends Openable implements IClassFile, IDocument {
protected BinaryPart binaryPart = null;
protected String name;
protected IProject project;
public static boolean SHARED_WC_VERBOSE = false;
private boolean sourceFileSearchRequired = true;
/**
* Constructs a handle to a ir file with the given name in the specified
* package.
*
* @param parent
* @param name
*
* @exception IllegalArgumentException if the name of the ir file does not end with ".ir"
*/
protected ClassFile(IEGLElement parent, String nameWithoutExtension) {
super(CLASS_FILE, parent, nameWithoutExtension);
this.name = nameWithoutExtension;
// if (!Util.isEGLIRFileName(name)) {
// throw new IllegalArgumentException(EGLModelResources.conventionUnitNotIRName);
// }
}
@Override
protected void buildStructure(OpenableElementInfo info, IProgressMonitor monitor) throws EGLModelException {
// Note: This method seems to be exactly the same as the one it overrides in Openable
if (monitor != null && monitor.isCanceled()) return;
// remove existing (old) infos
removeInfo();
HashMap newElements = new HashMap(11);
info.setIsStructureKnown(generateInfos(info, monitor, newElements, getResource()));
EGLModelManager.getEGLModelManager().getElementsOutOfSynchWithBuffers().remove(this);
for (Iterator iter = newElements.keySet().iterator(); iter.hasNext();) {
IEGLElement key = (IEGLElement) iter.next();
Object value = newElements.get(key);
EGLModelManager.getEGLModelManager().putInfo(key, value);
}
// add the info for this at the end, to ensure that a getInfo cannot reply null in case the LRU cache needs
// to be flushed. Might lead to performance issues.
// see PR 1G2K5S7: ITPJCORE:ALL - NPE when accessing source for a binary type
EGLModelManager.getEGLModelManager().putInfo(this, info);
}
@Override
protected boolean generateInfos(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource)
throws EGLModelException {
// put the info now, because getting the contents requires it
EGLModelManager.getEGLModelManager().putInfo(this, info);
ClassFileElementInfo unitInfo = (ClassFileElementInfo) info;
// generate structure
IRFileStructureRequestor requestor = new IRFileStructureRequestor(this, unitInfo, newElements);
BinaryElementParser parser = new BinaryElementParser(requestor,project);
parser.parseDocument(this, true);
// if (isWorkingCopy()) {
// EGLFile original = (EGLFile) getOriginalElement();
// // might be IResource.NULL_STAMP if original does not exist
// unitInfo.fTimestamp = ((IFile) original.getResource()).getModificationStamp();
// }
return unitInfo.isStructureKnown();
}
@Override
protected char getHandleMementoDelimiter() {
return EGLM_CLASSFILE;
}
@Override
public IPath getPath() {
PackageFragmentRoot root = this.getPackageFragmentRoot();
if (root.isArchive()) {
return root.getPath();
} else {
return this.getParent().getPath().append(this.getElementName());
}
}
@Override
public IResource getResource() {
PackageFragmentRoot root = this.getPackageFragmentRoot();
if (root.isArchive()) {
project = root.getParent().getEGLProject().getProject();
return root.getResource();
} else {
return ((IContainer)this.getParent().getResource()).getFile(new Path(this.getElementName()));
}
}
public IEGLElement rootedAt(IEGLProject project) {
return null;
}
public IPart[] getAllParts() throws EGLModelException {
return getParts();
}
public byte[] getBytes() throws EGLModelException {
EGLElement pkg = (EGLElement) getParent();
if (pkg instanceof EglarPackageFragment) {
EglarPackageFragmentRoot root = (EglarPackageFragmentRoot) pkg.getParent();
ZipFile zip = null;
try {
zip = root.getJar();
String entryName = CharOperation.concatWith(((PackageFragment) pkg).names, getElementName(), '/');
ZipEntry ze = zip.getEntry(entryName);
if (ze != null) {
return org.eclipse.edt.ide.core.internal.model.util.Util.getZipEntryByteContent(ze, zip);
}
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
} catch (IOException ioe) {
throw new EGLModelException(ioe, IEGLModelStatusConstants.IO_EXCEPTION);
} catch (CoreException e) {
if (e instanceof EGLModelException) {
throw (EGLModelException)e;
} else {
throw new EGLModelException(e);
}
} finally {
EGLModelManager.getEGLModelManager().closeZipFile(zip);
}
} else {
IFile file = (IFile) getResource();
return Util.getResourceContentsAsByteArray(file);
}
}
public IEGLElement getElementAt(int position) throws EGLModelException {
return null;
}
public IPackageDeclaration getPackageDeclaration(String name) {
return new PackageDeclaration(this, name);
}
public IPackageDeclaration[] getPackageDeclarations() throws EGLModelException {
ArrayList list = getChildrenOfType(PACKAGE_DECLARATION);
IPackageDeclaration[] array= new IPackageDeclaration[list.size()];
list.toArray(array);
return array;
}
public String[] getPackageName() {
IPackageFragment packageFragment = (IPackageFragment)getAncestor(IEGLElement.PACKAGE_FRAGMENT);
String[] packageName;
if(packageFragment.isDefaultPackage()){
packageName = new String[0];
}else{
packageName = packageFragment.getElementName().split("\\.");
}
return packageName;
}
public IPart getPart(String name) {
this.binaryPart = new BinaryPart(this, name);
return binaryPart;
}
public IPart[] getParts() throws EGLModelException {
ArrayList<IPart> list = getChildrenOfType(PART);
IPart[] array= new IPart[list.size()];
list.toArray(array);
return array;
}
public String getElementName() {
return this.name + EGL2IR.EGLXML;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.edt.ide.core.model.IClassFile#isClass()
*/
public boolean isClass() throws EGLModelException {
return true;
}
public String getSource() throws EGLModelException {
IBuffer buffer = getBuffer();
if (buffer == null) return ""; //$NON-NLS-1$
return buffer.getContents();
}
public ISourceRange getSourceRange() throws EGLModelException {
return ((EGLFileElementInfo) getElementInfo()).getSourceRange();
}
public void copy(IEGLElement container, IEGLElement sibling, String rename, boolean replace, IProgressMonitor monitor) throws EGLModelException {
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.READ_ONLY, this));
}
public void delete(boolean force, IProgressMonitor monitor) throws EGLModelException {
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.READ_ONLY, this));
}
public void move(IEGLElement container, IEGLElement sibling, String rename, boolean replace, IProgressMonitor monitor) throws EGLModelException {
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.READ_ONLY, this));
}
public void rename(String name, boolean replace, IProgressMonitor monitor) throws EGLModelException {
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.READ_ONLY, this));
}
public void commit(boolean force, IProgressMonitor monitor) throws EGLModelException {
throw new EGLModelException(new EGLModelStatus(IEGLModelStatusConstants.READ_ONLY, this));
}
public void destroy() {
}
public IEGLElement[] findElements(IEGLElement element) {
return null;
}
public IPart findPrimaryPart() {
return null;
}
public IEGLElement findSharedWorkingCopy(IBufferFactory bufferFactory) {
return null;
}
public IEGLElement getOriginal(IEGLElement workingCopyElement) {
return null;
}
public IEGLElement getOriginalElement() {
return null;
}
public IEGLElement getSharedWorkingCopy(IProgressMonitor monitor, IBufferFactory factory, IProblemRequestor problemRequestor)
throws EGLModelException {
//TODO
return null;
// // if factory is null, default factory must be used
// if (factory == null) factory = this.getBufferManager().getDefaultBufferFactory();
//
// EGLModelManager manager = EGLModelManager.getEGLModelManager();
//
// // In order to be shared, working copies have to denote the same egl file
// // AND use the same buffer factory.
// // Assuming there is a little set of buffer factories, then use a 2 level Map cache.
// Map sharedWorkingCopies = manager.sharedWorkingCopies;
//
// Map perFactoryWorkingCopies = (Map) sharedWorkingCopies.get(factory);
// if (perFactoryWorkingCopies == null){
// perFactoryWorkingCopies = new HashMap();
// sharedWorkingCopies.put(factory, perFactoryWorkingCopies);
// }
// WorkingCopy workingCopy = (WorkingCopy)perFactoryWorkingCopies.get(this);
// if (workingCopy != null) {
// workingCopy.useCount++;
//
// if (SHARED_WC_VERBOSE) {
// System.out.println("Incrementing use count of shared working copy " + workingCopy.toStringWithAncestors()); //$NON-NLS-1$
// }
//
// return workingCopy;
// } else {
// CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(this, perFactoryWorkingCopies, factory, problemRequestor);
// op.runOperation(monitor);
// return op.getResultElements()[0];
// }
}
public IEGLElement getWorkingCopy() throws EGLModelException {
return null;
}
public IEGLElement getWorkingCopy(IProgressMonitor monitor, IBufferFactory factory, IProblemRequestor problemRequestor) throws EGLModelException {
return null;
}
public boolean isBasedOn(IResource resource) {
return false;
}
public boolean isWorkingCopy() {
return false;
}
public IMarker[] reconcile() throws EGLModelException {
return null;
}
public void reconcile(boolean forceProblemDetection, IProgressMonitor monitor) throws EGLModelException {
}
public void restore() throws EGLModelException {
}
public byte[] getByteContent() throws IOException {
try {
return getBytes();
} catch (EGLModelException e) {
return null;
}
}
public char[] getCharContent() throws IOException {
// TODO Auto-generated method stub
return null;
}
public String getEncoding() {
// TODO Auto-generated method stub
return null;
}
public String getStringContent() throws IOException {
// TODO Auto-generated method stub
return null;
}
public String getType() {
return "eglxml";
}
public IPart getPart() {
try {
IPart[] parts = getAllParts();
if(parts.length > 0) {
return parts[0];
}
} catch (EGLModelException e) {
e.printStackTrace();
}
return null;
}
public String getTypeName() {
// Internal class file name doesn't contain ".class" file extension
// int lastDollar = this.name.lastIndexOf('$');
return this.name;
// return lastDollar > -1 ? Util.localTypeName(this.name, lastDollar, this.name.length()) : this.name;
}
public String getName() {
return getFileName().toString();
}
public char[] getFileName(){
return getElementName().toCharArray();
}
protected OpenableElementInfo createElementInfo() {
return new ClassFileElementInfo();
}
public IClassFile getClassFile() {
return this;
}
protected boolean hasBuffer() {
return true;
}
protected IBuffer openBuffer(IProgressMonitor pm, Object info) {
// Check the cache for the top-level type first
// IType outerMostEnclosingType = getOuterMostEnclosingType();
IBuffer buffer = getBufferManager().getBuffer(this);
if (buffer == null) {
SourceMapper mapper = getSourceMapper();
//info: ClassFileElementInfo
ClassFileElementInfo classFileInfo = info instanceof ClassFileElementInfo? (ClassFileElementInfo)info : null;
if (mapper != null) {
buffer = mapSource(mapper, classFileInfo, this.getClassFile());
}
}
return buffer;
}
/** Loads the buffer via SourceMapper, and maps it in SourceMapper */
private IBuffer mapSource(SourceMapper mapper, ClassFileElementInfo info, IClassFile bufferOwner) {
char[] contents = mapper.findSource(getPart(), info.getEglFileName(), info.getCaseSensitivePackageName());
// create buffer
BufferManager bufManager = getBufferManager();
IBuffer buffer = bufManager.createBuffer(bufferOwner);
if (buffer == null)
return null;
bufManager.addBuffer(buffer);
if (contents != null) {
// set the buffer source
if (buffer.getCharacters() == null){
buffer.setContents(contents);
}
} else {
if (buffer.getCharacters() == null){
String result = EGLModelResources.eglarNoSourceAttachmentContent;
buffer.setContents(result.toCharArray());
}
}
buffer.addBufferChangedListener(this);
return buffer;
}
public SourceMapper getSourceMapper(){
/*
* case 1: if the binary project is imported into workspace, and the eglar file is in this imported binary project,
* code enters here when:
* 1) have decided this classfile is under BP but no corresponding source file found in the source folder. Enter
* case #3 directly
* 2) need to decide if this classfile is under BP (if is BP but no source file available, enter case #3 eventually)
* case 2: if project A refers binary project B, and binary project B is used in Target Platform and remains external,
* corresponding eglar file of B will be attached as a library of A. To open an ir in project B's eglar, first look
* for external source folder for project B. if B has external source folder and corresponding source file found for
* the ir, then create SourceMapper using the external source folder location; otherwise, goto case #3
* case 3: source folder (either internal or external) not available, or no corresponding source file found in the source
* folder, then use the source attachment location to create SourceMapper, which should be the same as its parent's
* SourceMapper (eventually, this should be the same as the eglar SourceMapper)
*
*/
//case 1:
if(sourceFileSearchRequired && ResourcesPlugin.getWorkspace().getRoot().findMember(this.getPath()) != null){//non-external eglar file
IPath projPath = this.getEGLProject().getProject().getLocation(); //absolute location in file system
if(org.eclipse.edt.ide.core.internal.model.util.Util.isBinaryProject(new File(projPath.toString()))){//is binary project
String[] eglSourceFolders = org.eclipse.edt.ide.core.internal.model.util.Util.getEGLSourceFolders(new File(this.getEGLProject().getProject().getLocation().toString()));
for(String eglSourceFolder: eglSourceFolders){
IResource sourceFolder = this.getEGLProject().getProject().findMember(eglSourceFolder);
if(sourceFolder != null && sourceFolder.exists() && sourceFolder.getType() == IResource.FOLDER){
String pkgPath;
try {
pkgPath = this.getPackageDeclarations()[0].getElementName();
pkgPath = pkgPath.replace(".", File.separator);
IResource fullPkgFolder = ((IFolder)sourceFolder).findMember(pkgPath);
if(fullPkgFolder != null && fullPkgFolder.exists() && fullPkgFolder.getType() == IResource.FOLDER){ //package matches
ClassFileElementInfo elementInfo = ((ClassFileElementInfo)this.getElementInfo());
String srcName = elementInfo.getEglFileName();
IResource sourceFile = ((IFolder)fullPkgFolder).findMember(srcName);
if(sourceFile == null) {
IPath path = new Path(srcName);
srcName = path.lastSegment();
sourceFile = ((IFolder)fullPkgFolder).findMember(srcName);
}
if(sourceFile != null && sourceFile.exists() && sourceFile.getType() == IResource.FILE){ //egl source matches, use the source
return new SourceMapper(
sourceFile.getFullPath(),
null,
getEGLProject().getOptions(true));
}
}
} catch (EGLModelException e) {
e.printStackTrace();
}
}
}
}
}
//case 2:
if(ResourcesPlugin.getWorkspace().getRoot().findMember(this.getPath()) == null){ //external eglar file
String eglarPath = this.getPath().toString();
IEGLProject eglProj = this.getEGLProject();
boolean isInBinaryProj = false;
try {
IEGLPathEntry[] pathEntries = eglProj.getResolvedEGLPath(true);
for(IEGLPathEntry entry: pathEntries){
//if the entry path represents a binary project, and the entry path
//is the one we want to open, then look for the binary project's source folder
if(entry.isBinaryProject() && entry.getPath().equals(this.getPath())){
isInBinaryProj = true;
break;
}
}
} catch (EGLModelException e1) {
e1.printStackTrace();
}
if(isInBinaryProj){ //is in binary project
if(org.eclipse.edt.ide.core.internal.model.Util.isEGLARFileName(eglarPath)){
int index = eglarPath.lastIndexOf("/");
if(index == -1){
index = eglarPath.lastIndexOf(File.separator);
}
if(index != -1){
String projRootPath = eglarPath.substring(0, index);
String[] eglSourceFolders = org.eclipse.edt.ide.core.internal.model.util.Util.getEGLSourceFolders(new File(projRootPath));
for(String eglSourceFolder: eglSourceFolders){
String sourcePath = projRootPath + File.separator + eglSourceFolder;
//try to find the source file
try {
String pkgPath = this.getPackageDeclarations()[0].getElementName();
index = pkgPath.indexOf(".");
while(index != -1){
sourcePath += File.separator + pkgPath.substring(0, index);
pkgPath = pkgPath.substring(index + 1);
index = pkgPath.indexOf(".");
}
sourcePath += File.separator + pkgPath;
//the ir file name is not always equal to the source egl file, as one egl file can relate to
//several ir files (depends on how many Parts in the egl file)
String srcName = ((ClassFileElementInfo)this.getElementInfo()).getEglFileName();
if(pkgPath.trim().length() > 0)
sourcePath += File.separator;
sourcePath += srcName;
return new SourceMapper(
new Path(sourcePath),
null,
getEGLProject().getOptions(true));
} catch (EGLModelException e) {
e.printStackTrace();
}
}
}
}
}
}
//case 3:
return super.getSourceMapper();
}
//find current ClassFile's corresponding eclipse file from source folder
public IFile getFileInSourceFolder(){
String[] eglSourceFolders = org.eclipse.edt.ide.core.internal.model.util.Util.getEGLSourceFolders(new File(this.getEGLProject().getProject().getLocation().toString()));
for(String eglSourceFolder: eglSourceFolders){
IResource sourceFolder = this.getEGLProject().getProject().findMember(eglSourceFolder);
if(sourceFolder != null && sourceFolder.exists() && sourceFolder.getType() == IResource.FOLDER){ //source folder exists
String pkgPath = "";
IResource fullPkgFolder = null;
try {
ClassFileElementInfo elementInfo = ((ClassFileElementInfo)this.getElementInfo());
String[] pkgs = elementInfo.getCaseSensitivePackageName();
if(pkgs != null && pkgs.length > 0){
for(int i=0; i<pkgs.length-1; i++){
pkgPath += pkgs[i];
pkgPath += File.separator;
}
pkgPath += pkgs[pkgs.length - 1];
fullPkgFolder = ((IFolder)sourceFolder).findMember(pkgPath);
}
else{
fullPkgFolder = sourceFolder;
}
if(fullPkgFolder != null && fullPkgFolder.exists() && fullPkgFolder.getType() == IResource.FOLDER){ //package matches
String srcName = elementInfo.getEglFileName(); //srcName should represent the source file name eliminating package name
int index = srcName.lastIndexOf("/");
if(index != -1){
srcName = srcName.substring(index);
}
IResource sourceFile = ((IFolder)fullPkgFolder).findMember(srcName);
if(sourceFile != null && sourceFile.exists() && sourceFile.getType() == IResource.FILE){ //egl source matches
return (IFile)sourceFile;
}
}
} catch (EGLModelException e) {
e.printStackTrace();
}
}
}
return null;
}
public void setSourceFileSearchRequired(boolean sourceFileSearchRequired) {
this.sourceFileSearchRequired = sourceFileSearchRequired;
}
@Override
public boolean exists() {
if (getParent() instanceof EglarPackageFragment) {
try {
IClassFile[] classes = ((EglarPackageFragment)getParent()).getClassFiles();
for (IClassFile next : classes) {
// Package fragment returns its known child instance of IRs that exist, but returns a dummy instance for IRs
// that do not exist (since getClassFile() is a handle method - never returns null).
if (next == this) {
return true;
}
}
}
catch (EGLModelException eme) {
}
return false;
}
return super.exists();
}
}