/*******************************************************************************
* Copyright (c) 2006-2013
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* 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:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.emftext.language.java.resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.emftext.language.java.JavaClasspath;
import org.emftext.language.java.JavaUniquePathConstructor;
import org.emftext.language.java.classifiers.ConcreteClassifier;
import org.emftext.language.java.commons.NamedElement;
import org.emftext.language.java.containers.CompilationUnit;
import org.emftext.language.java.containers.ContainersFactory;
import org.emftext.language.java.containers.EmptyModel;
import org.emftext.language.java.containers.Package;
import org.emftext.language.java.members.Member;
import org.emftext.language.java.members.MemberContainer;
import org.emftext.language.java.members.MembersPackage;
import org.emftext.language.java.resource.java.IJavaContextDependentURIFragment;
import org.emftext.language.java.resource.java.IJavaContextDependentURIFragmentFactory;
import org.emftext.language.java.resource.java.IJavaInputStreamProcessorProvider;
import org.emftext.language.java.resource.java.IJavaOptions;
import org.emftext.language.java.resource.java.IJavaReferenceResolverSwitch;
import org.emftext.language.java.resource.java.IJavaTextPrinter;
import org.emftext.language.java.resource.java.mopp.JavaInputStreamProcessor;
import org.emftext.language.java.resource.java.mopp.JavaParser;
import org.emftext.language.java.resource.java.mopp.JavaResource;
import org.emftext.language.java.resource.java.util.JavaLayoutUtil;
import org.emftext.language.java.resource.java.util.JavaUnicodeConverter;
import org.emftext.language.java.util.JavaModelCompletion;
/**
* A resource that uses either the generated {@link JavaParser} or the
* {@link ClassFileModelLoader} for loading depending on the file extension of
* the resource's URI.
*/
public class JavaSourceOrClassFileResource extends JavaResource {
private final JavaLayoutUtil layoutUtil = new JavaLayoutUtil();
public JavaSourceOrClassFileResource(URI uri) {
super(uri);
}
protected boolean isClassFile() {
if (uri == null) {
return false;
}
// Is there a physical source file behind this URI?
URI normalizedURI = getURIConverter().normalize(uri);
String fileExtension = normalizedURI.fileExtension();
if ("class".equals(fileExtension)) {
return true;
}
return false;
}
protected boolean isPackage() {
List<EObject> contents = getContentsInternal();
if (contents.isEmpty()) {
return false;
}
return contents.get(0) instanceof Package;
}
protected boolean hasJavaClassifierURI() {
if (uri == null) {
return false;
}
return uri.toString().startsWith(
JavaUniquePathConstructor.JAVA_CLASSIFIER_PATHMAP);
}
@Override
protected void doLoad(InputStream inputStream, Map<?, ?> options)
throws IOException {
if (isClassFile()) {
JavaClasspath javaClasspath = JavaClasspath.get(this);
ClassFileModelLoader classFileParser = new ClassFileModelLoader(javaClasspath);
CompilationUnit cu = classFileParser.parse(inputStream, getURI().lastSegment());
getContentsInternal().add(cu);
JavaModelCompletion.complete(this);
} else {
Map<Object, Object> optionsWithUnicodeConverter = new LinkedHashMap<Object, Object>();
if (options != null) {
optionsWithUnicodeConverter.putAll(options);
}
if (!optionsWithUnicodeConverter.containsKey(IJavaOptions.INPUT_STREAM_PREPROCESSOR_PROVIDER)) {
optionsWithUnicodeConverter.put(
IJavaOptions.INPUT_STREAM_PREPROCESSOR_PROVIDER,
new IJavaInputStreamProcessorProvider() {
public JavaInputStreamProcessor getInputStreamProcessor(InputStream inputStream) {
return new JavaUnicodeConverter(inputStream);
}
});
}
super.doLoad(inputStream, optionsWithUnicodeConverter);
if (getContentsInternal().isEmpty() && getErrors().isEmpty()) {
contents.add(ContainersFactory.eINSTANCE.createEmptyModel());
}
}
}
@Override
public void load(Map<?, ?> options) throws IOException {
URIConverter uriConverter = getURIConverter();
URI normalizedURI = uriConverter.normalize(uri);
String normalizedURIString = normalizedURI.toString();
if (normalizedURIString.startsWith(JavaUniquePathConstructor.JAVA_PACKAGE_PATHMAP)) {
if (!isLoaded) {
loadPackageFromClasspath();
}
} else if (normalizedURIString.startsWith(JavaUniquePathConstructor.JAVA_CLASSIFIER_PATHMAP)) {
//classes should have a physical resource
//System.out.println("[JaMoPP] Warning: " + uri.lastSegment() + " not registered in class path");
} else {
super.load(options);
}
register();
}
@Override
protected void doUnload() {
List<EObject> contentsInternal = getContentsInternal();
if (!contentsInternal.isEmpty()) {
if (contentsInternal.get(0) instanceof Package) {
contentsInternal.clear();
} else {
super.doUnload();
}
}
}
/**
* We override this method to enhance the created proxy objects by setting
* the 'name' attribute. This is needed to ask proxy objects for their name
* without resolving them.
*/
public <ContainerType extends EObject, ReferenceType extends EObject> void registerContextDependentProxy(IJavaContextDependentURIFragmentFactory<ContainerType, ReferenceType> factory, ContainerType container, EReference reference, String id, EObject proxyElement, int position) {
super.registerContextDependentProxy(factory, container, reference, id, proxyElement, position);
if (proxyElement instanceof NamedElement) {
NamedElement namedElement = (NamedElement) proxyElement;
namedElement.setName(id);
}
}
@Override
public EObject getEObject(String id) {
EObject result = null;
// check whether proxy resolving is turned off
Object disableProxyResolvingValue = getResourceSet().getLoadOptions().get(IExtendedJavaOptions.DISABLE_ON_DEMAND_PROXY_RESOLVING);
if (Boolean.TRUE.equals(disableProxyResolvingValue)) {
return null;
}
if ((isClassFile() || isPackage()) &&
id.startsWith("//" + JavaUniquePathConstructor.CLASSIFIERS_ROOT_PATH_PREFIX)) {
if (!getContentsInternal().isEmpty()) {
//in a class file, there is always only one classifier as root element:
//id path can be ignored
CompilationUnit cu = (CompilationUnit) contents.get(0);
return cu.getClassifiers().get(0);
} else {
//if this happens, there was a problem during class file loading
return null;
}
}
else {
try {
result = super.getEObject(id);
} catch (Exception e) {
e.printStackTrace();
}
if (!id.startsWith(IJavaContextDependentURIFragment.INTERNAL_URI_FRAGMENT_PREFIX)) {
if (result != null && !(result instanceof ConcreteClassifier)) {
//may happen if members of same name exist
if (result.eContainingFeature() != null
&& result.eContainingFeature().equals(MembersPackage.Literals.MEMBER_CONTAINER__MEMBERS)
&& result instanceof NamedElement) {
String memberName = ((NamedElement) result).getName();
for (Member m : ((MemberContainer) result.eContainer()).getMembers()) {
if (memberName.equals(m.getName()) && m instanceof ConcreteClassifier) {
result = m;
return result;
}
}
}
}
}
}
return result;
}
@Override
protected EObject getEObject(List<String> uriFragmentPath) {
int size = uriFragmentPath.size();
EObject eObject = getEObjectForURIFragmentRootSegment(size == 0 ? ""
: uriFragmentPath.get(0));
for (int i = 1; i < size && eObject != null; ++i) {
String uriFragment = uriFragmentPath.get(i);
if (eObject instanceof MemberContainer && uriFragment.startsWith(
JavaUniquePathConstructor.CLASSIFIERS_SUB_PATH_PREFIX)) {
MemberContainer memberContainer = (MemberContainer) eObject;
String name = uriFragment.substring(
JavaUniquePathConstructor.CLASSIFIERS_SUB_PATH_PREFIX.length(),
uriFragment.length() - 2);
eObject = memberContainer.getContainedClassifier(name);
}
else if (eObject instanceof CompilationUnit && uriFragment.startsWith(
JavaUniquePathConstructor.CLASSIFIERS_ROOT_PATH_PREFIX)){
CompilationUnit compilationUnit = (CompilationUnit)eObject;
String name = uriFragment.substring(
JavaUniquePathConstructor.CLASSIFIERS_ROOT_PATH_PREFIX.length(),
uriFragment.length() - 2);
eObject = compilationUnit.getContainedClassifier(name);
int j = i + 1;
while (j < size && eObject == null) {
// this is required for classifiers with '$' in their names
String subUriFragment = uriFragmentPath.get(j);
name = name + "$" + subUriFragment.substring(
JavaUniquePathConstructor.CLASSIFIERS_SUB_PATH_PREFIX.length(),
subUriFragment.length() - 2);
eObject = compilationUnit.getContainedClassifier(name);
if (eObject != null) {
i = j;
} else {
j++;
}
}
} else {
eObject = ((InternalEObject)eObject).eObjectForURIFragmentSegment(uriFragmentPath.get(i));
}
}
return eObject;
}
protected void loadPackageFromClasspath() {
Package thePackage = ContainersFactory.eINSTANCE.createPackage();
String packageName = getURI().trimFileExtension().toString().substring(
JavaUniquePathConstructor.JAVA_PACKAGE_PATHMAP.length());
String[] packageNameParts = packageName.split("\\.");
for (int i = 0; i < packageNameParts.length; i++) {
if (i < packageNameParts.length - 1) {
thePackage.getNamespaces().add(packageNameParts[i]);
}
else {
thePackage.setName(packageNameParts[i]);
}
}
populatePackage(thePackage);
getContentsInternal().add(thePackage);
}
protected void register() throws IOException {
URI myURI = getURI();
if (!getContentsInternal().isEmpty()) {
EObject root = getContentsInternal().get(0);
if (root instanceof CompilationUnit) {
CompilationUnit cu = (CompilationUnit) root;
setCompilationUnitName(cu);
}
//only for physical URIs
if(hasJavaClassifierURI()) {
return;
}
//could also be a package-info.java without CU
if (root instanceof CompilationUnit) {
CompilationUnit cu = (CompilationUnit) root;
JavaClasspath.get(this).registerClassifierSource(cu, myURI);
} else if (root instanceof Package) {
//package-info.java
Package p = (Package) root;
populatePackage(p);
} else if (root instanceof EmptyModel) {
((EmptyModel) root).setName(myURI.trimFileExtension().lastSegment());
}
}
}
protected void setCompilationUnitName(CompilationUnit cu) {
String packageName = "";
if (!hasJavaClassifierURI()) {
//physical URIs do not include the package name
//so we construct it from the cu's namespaces
packageName = JavaUniquePathConstructor.packageName(cu);
}
String fileName = getURI().lastSegment();
if (!"".equals(packageName)) {
cu.setName(packageName + "." + fileName);
} else {
cu.setName(fileName);
}
}
protected void populatePackage(Package p) {
String packageName = JavaUniquePathConstructor.packageName(p);
String fullPackageName = packageName + "." + p.getName();
JavaClasspath classpath = JavaClasspath.get(this);
List<EObject> classifiers = classpath.getClassifiers(
fullPackageName, "*");
for (EObject classifier : classifiers) {
classifier = (ConcreteClassifier) EcoreUtil.resolve(classifier, getResourceSet());
if (classifier.eIsProxy()) {
// Skip proxies
continue;
}
CompilationUnit cu = (CompilationUnit)classifier.eContainer();
if (cu == null) {
continue;
}
p.getCompilationUnits().add(cu);
}
}
@Override
protected void doSave(OutputStream outputStream, Map<?, ?> options)
throws IOException {
if (isClassFile()) {
// Saving is not supported for class files.
return;
}
ResourceSet resourceSetForSave = getResourceSet();
if (resourceSetForSave == null) {
resourceSetForSave = new ResourceSetImpl();
}
if (containsMultipleCompilationUnits()) {
for (EObject eObject : new BasicEList<EObject>(getContentsInternal())) {
if (eObject instanceof CompilationUnit) {
CompilationUnit cu = (CompilationUnit) eObject;
if (cu.getClassifiers().isEmpty()) {
continue;
}
String[] folder = cu.getNamespaces().toArray(
new String[cu.getNamespaces().size()]);
String file = cu.getClassifiers().get(0).getName();
URI normalizedURI = resourceSetForSave.getURIConverter().normalize(getURI());
URI subResourcURI = normalizedURI.trimFileExtension().trimFileExtension();
if (normalizedURI.segmentCount() >= folder.length + 1 &&
normalizedURI.segmentsList().subList(
normalizedURI.segmentCount() - 1 - folder.length,
normalizedURI.segmentCount() - 1).equals(Arrays.asList(folder))) {
subResourcURI = subResourcURI.trimSegments(1);
} else {
subResourcURI = subResourcURI.appendSegments(folder);
}
subResourcURI = subResourcURI.appendSegment(file);
subResourcURI = subResourcURI.appendFileExtension("java");
Resource subResource =
resourceSetForSave.createResource(subResourcURI);
addPackageDeclaration(cu);
subResource.getContents().add(cu);
subResource.save(options);
} else {
//nothing
}
}
} else {
if (!getContentsInternal().isEmpty()) {
if (getContentsInternal().get(0) instanceof CompilationUnit) {
CompilationUnit cu = (CompilationUnit) getContentsInternal().get(0) ;
addPackageDeclaration(cu);
}
//super.doSave(outputStream, options);
IJavaTextPrinter printer = getMetaInformation().createPrinter(outputStream, this);
IJavaReferenceResolverSwitch referenceResolverSwitch = getReferenceResolverSwitch();
printer.setEncoding(getEncoding(options));
printer.setOptions(options);
referenceResolverSwitch.setOptions(options);
EObject root = getContentsInternal().get(0); //only print the single CU or Package
if (isLayoutInformationRecordingEnabled()) {
layoutUtil.transferAllLayoutInformationFromModel(root);
}
printer.print(root);
if (isLayoutInformationRecordingEnabled()) {
layoutUtil.transferAllLayoutInformationToModel(root);
}
}
}
}
protected boolean containsMultipleCompilationUnits() {
boolean foundOne = false;
for (EObject eObject : getContentsInternal()) {
if (eObject instanceof CompilationUnit) {
if (foundOne) {
return true;
}
foundOne = true;
}
}
return false;
}
/**
* This method adds a package declaration (namespaces) to the given
* compilation unit if none is defined and this resource has a logical URI.
* The segments of the logical URI are assumed as package name.
*/
protected void addPackageDeclaration(CompilationUnit cu) {
URI uri = getURI();
if (cu.getNamespaces().isEmpty() && !uri.isFile() && !uri.isPlatform()) {
// If there is no package and this is a logical URI, we guess the
// package based on the URI.
String[] fullName = uri.lastSegment().split("\\.");
for (int i = 0; i < fullName.length - 2; i++) {
cu.getNamespaces().add(fullName[i]);
}
}
}
}