/*
* Copyright (c) 2006, 2009 Borland Software 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:
* Artem Tikhomirov (Borland)
* Boris Blajer (Borland) - support for composite resources
*/
package org.eclipse.gmf.internal.xpand.util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.gmf.internal.xpand.Activator;
import org.eclipse.gmf.internal.xpand.ResourceManager;
import org.eclipse.gmf.internal.xpand.model.XpandResource;
import org.eclipse.gmf.internal.xpand.xtend.ast.QvtFile;
import org.eclipse.gmf.internal.xpand.xtend.ast.QvtResource;
import org.eclipse.m2m.internal.qvt.oml.common.MdaException;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompiledUnit;
import org.eclipse.m2m.internal.qvt.oml.compiler.QVTOCompiler;
import org.eclipse.m2m.internal.qvt.oml.compiler.QvtCompilerOptions;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitProxy;
import org.eclipse.m2m.internal.qvt.oml.compiler.UnitResolver;
// FIXME it's not a good idea to parse file on every proposal computation
public abstract class ResourceManagerImpl implements ResourceManager {
private final Map<String, XpandResource> cachedXpand = new TreeMap<String, XpandResource>();
private final Map<String, QvtResource> cachedQvt = new TreeMap<String, QvtResource>();
private QVTOCompiler qvtCompiler;
private QvtCompilerOptions qvtCompilerOptions;
public QvtResource loadQvtResource(String fullyQualifiedName) {
try {
return loadQvtResourceThroughCache(fullyQualifiedName);
} catch (FileNotFoundException ex) {
return null; // Missing resource is an anticipated situation, not a
// error that should be handled
} catch (IOException e) {
Activator.logError(e);
} catch (ParserException e) {
// TODO: check if any exceptions present here at all..
handleParserException(e);
}
return null;
}
protected QvtResource loadQvtResourceThroughCache(String qualifiedName) throws IOException, ParserException {
if (hasCachedQvt(qualifiedName)) {
return cachedQvt.get(qualifiedName);
}
final QvtResource loaded = doLoadQvtResource(qualifiedName);
assert loaded != null; // this is the contract of loadXtendResource
if (shouldCache()) {
cachedQvt.put(qualifiedName, loaded);
}
return loaded;
}
private QvtResource doLoadQvtResource(String fullyQualifiedName) throws IOException, ParserException {
String compilationUnitQName = fullyQualifiedName.replace(TypeNameUtil.NS_DELIM, "."); //$NON-NLS-1$
CompiledUnit compiledUnit = null;
try {
UnitProxy unitProxy = getQVTUnitResolver().resolveUnit(compilationUnitQName);
if (unitProxy == null) {
throw new FileNotFoundException("Failed to resolve: " + fullyQualifiedName); //$NON-NLS-1$
}
compiledUnit = getQvtCompiler().compile(unitProxy, getQvtCompilerOptions(), null);
} catch (MdaException e) {
throw new FileNotFoundException(fullyQualifiedName);
}
if (compiledUnit == null) {
throw new FileNotFoundException(fullyQualifiedName);
}
return new QvtFile(compiledUnit, fullyQualifiedName);
}
abstract protected String resolveCFileFullPath(String fullyQualifiedName, String fileExtension);
/**
* Using singleton QvtCompiler instance with "history". To prevent same
* (native) libraries from being loaded twice into if (indirectly)
* references by two different XpandResources.
*/
private QVTOCompiler getQvtCompiler() {
if (qvtCompiler == null) {
// TODO: use different kind of ImportResolver being able to
// construct referenced CFiles using ResourceManagerImpl
qvtCompiler = QVTOCompiler.createCompilerWithHistory(getMetamodelResourceSet());
}
return qvtCompiler;
}
protected ResourceSet getMetamodelResourceSet() {
return Activator.getWorkspaceMetamodelsResourceSet();
}
private QvtCompilerOptions getQvtCompilerOptions() {
if (qvtCompilerOptions == null) {
qvtCompilerOptions = new QvtCompilerOptions();
qvtCompilerOptions.setGenerateCompletionData(true);
qvtCompilerOptions.setShowAnnotations(false);
}
return qvtCompilerOptions;
}
public XpandResource loadXpandResource(String fullyQualifiedName) {
try {
return loadXpandThroughCache(fullyQualifiedName);
} catch (FileNotFoundException ex) {
// Missing resource is an anticipated situation, not a error that should be handled
return null;
} catch (IOException ex) {
// XXX come up with better handling
Activator.logWarn(ex.getMessage());
} catch (ParserException ex) {
handleParserException(ex);
}
return null;
}
protected XpandResource loadXpandThroughCache(String qualifiedName) throws IOException, ParserException {
if (hasCachedXpand(qualifiedName)) {
return cachedXpand.get(qualifiedName);
}
final XpandResource loaded = doLoadXpandResource(qualifiedName);
if (shouldCache()) {
cachedXpand.put(qualifiedName, loaded);
}
return loaded;
}
private XpandResource doLoadXpandResource(String fullyQualifiedName) throws IOException, ParserException {
Reader[] rs1 = resolveMultiple(fullyQualifiedName, XpandResource.TEMPLATE_EXTENSION);
assert rs1 != null && rs1.length > 0; // exception should be thrown to
// indicate issues with resolve
XpandResource[] unadvised = loadXpandResources(rs1, fullyQualifiedName);
XpandResource[] advices = null;
try {
String aspectsTemplateName = getAspectsTemplateName(fullyQualifiedName);
Reader[] rs2 = resolveMultiple(aspectsTemplateName, XpandResource.TEMPLATE_EXTENSION);
// XXX relax resolveMultiple to return empty array and use length==0
// here instead of exception
advices = loadXpandResources(rs2, aspectsTemplateName);
} catch (FileNotFoundException e) {
} catch (IOException ex) {
// XXX come up with better handling
Activator.logWarn(ex.getMessage());
} catch (ParserException ex) {
handleParserException(ex);
}
if (advices == null && unadvised.length == 1) {
return unadvised[0];
}
return new CompositeXpandResource(this, unadvised, advices);
}
/**
* XXX: only to simplify tests, should be private or inlined
*/
protected String getAspectsTemplateName(String fullyQualifiedName) {
return ASPECT_PREFIX + fullyQualifiedName;
}
/**
* If the given fully-qualified name is an aspect, transforms it to its
* "host" fully-qualified name. Otherwise, returns the given fully-qualified
* name.
*/
protected String getNonAspectsTemplateName(String possiblyAspectedFullyQualifiedName) {
if (possiblyAspectedFullyQualifiedName == null) {
return null;
}
if (possiblyAspectedFullyQualifiedName.startsWith(ASPECT_PREFIX)) {
return possiblyAspectedFullyQualifiedName.substring(ASPECT_PREFIX.length());
}
return possiblyAspectedFullyQualifiedName;
}
protected abstract void handleParserException(ParserException ex);
/**
* Returns an array of resolutions, in the order from newest to oldest. This
* is to enable one template to partially override only a subset of parent
* templates.
*
* @return never return <code>null</code> or an empty array, throw exception
* instead
* @throws IOException
* in case resource can't be read. Throw
* {@link java.io.FileNotFoundException} to indicate resource
* was not found.
*/
protected abstract Reader[] resolveMultiple(String fullyQualifiedName, String extension) throws IOException;
/**
* Readers get closed after parse attempt.
*/
protected XpandResource[] loadXpandResources(Reader[] readers, String fullyQualifiedName) throws IOException, ParserException {
XpandResource[] result = new XpandResource[readers.length];
for (int i = 0; i < readers.length; i++) {
assert readers[i] != null;
try {
result[i] = new XpandResourceParser().parse(readers[i], fullyQualifiedName);
assert result[i] != null; // this is the contract of parse
} finally {
try {
readers[i].close();
} catch (Exception ex) {/* IGNORE */
}
}
}
return result;
}
protected abstract boolean shouldCache();
protected final boolean hasCachedXpand(String fullyQualifiedName) {
return shouldCache() && cachedXpand.containsKey(fullyQualifiedName);
}
protected final boolean hasCachedQvt(String fullyQualifiedName) {
return shouldCache() && cachedQvt.containsKey(fullyQualifiedName);
}
protected final void forgetCachedXpand(String fullyQualifiedName) {
cachedXpand.remove(fullyQualifiedName);
}
protected final void forgetCachedQvt(String fullyQualifiedName) {
cachedQvt.remove(fullyQualifiedName);
}
protected final void forgetAll() {
cachedXpand.clear();
cachedQvt.clear();
qvtCompiler = null;
}
protected abstract UnitResolver getQVTUnitResolver();
private static final String ASPECT_PREFIX = "aspects" + TypeNameUtil.NS_DELIM; //$NON-NLS-1$
}