/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* DeserializationHelper.java
* Creation date: Jan 13, 2006.
* By: Edward Lam
*/
package org.openquark.cal.compiler;
import java.util.Map;
import org.openquark.cal.compiler.CompilerMessage.Identifier;
import org.openquark.cal.internal.compiler.ForeignEntityResolver;
import org.openquark.cal.internal.compiler.StandaloneRuntimeForeignClassResolver;
/**
* Warning- this class should only be used by the CAL compiler implementation. It is not part of the
* external API of the CAL platform.
* <p>
* A helper class containing utility methods used in module deserialization.
* @author Edward Lam
*/
public abstract class DeserializationHelper {
/** Map from class name to class for method classForName() */
static final Map<String, Class<?>> nameToClassMap = StandaloneRuntimeForeignClassResolver.nameToClassMap;
/*
* Not intended to be instantiated.
*/
private DeserializationHelper() {
}
/**
* Check the schema from the serialized record against the current schema.
* @param savedSchema the schema of the saved record.
* @param currentSchema the currently-known schema number.
* @param moduleName the name of the module being deserialized.
* @param internalName the internal name of the record being deserialized.
* @param msgLogger the logger to which to log deserialization messages.
* @throws CompilerMessage.AbortCompilation if the saved schema is incompatible with the current schema.
* For the time being, this is used to abort loading the rest of the module.
*/
public static void checkSerializationSchema(final int savedSchema, final int currentSchema, final ModuleName moduleName, final String internalName, final CompilerMessageLogger msgLogger) {
if (savedSchema > currentSchema) {
final SourcePosition startSourcePosition = new SourcePosition(0, 0, moduleName.toSourceText());
final SourcePosition endSourcePosition = new SourcePosition(0, 1, moduleName.toSourceText());
final SourceRange sourceRange = new SourceRange(startSourcePosition, endSourcePosition);
final MessageKind messageKind = new MessageKind.Error.DeserializedIncompatibleSchema(savedSchema, currentSchema, internalName);
msgLogger.logMessage(new CompilerMessage(sourceRange, messageKind));
}
}
/**
* Constructs a ForeignEntityProvider encapsulating the resolution logic of {@link #classForName}.
* @param className
* @param foreignClassLoader the classloader to use to resolve foreign classes.
* @param recordLoadName the name of the record being loaded.
* This will be used if an error occurs finding the java class.
* @param associatedEntity the name of the associated CAL entity. Can be null an associated entity cannot be provided.
* @param msgLogger the logger to which to log deserialization messages.
* @return a ForeignEntityProvider instance.
*/
static ForeignEntityProvider<Class<?>> classProviderForName(final String className, final ClassLoader foreignClassLoader, final String recordLoadName, final Identifier associatedEntity, final CompilerMessageLogger msgLogger) {
return ForeignEntityProvider.make(msgLogger, new ForeignEntityProvider.Resolver<Class<?>>(className) {
@Override
public Class<?> resolve(final ForeignEntityProvider.MessageHandler messageHandler) throws UnableToResolveForeignEntityException {
return classForName(className, foreignClassLoader, recordLoadName, associatedEntity, messageHandler);
}
});
}
/**
* @param associatedEntity the name of the associated CAL entity. Can be null an associated entity cannot be provided.
* @return null if associatedEntity is null.
* Otherwise, a source range for the module from the provided name, consisting of source name only (no positions).
*/
private static SourceRange getSourceRangeForAssociatedEntity(final CompilerMessage.Identifier associatedEntity) {
if (associatedEntity == null) {
return null;
}
return new SourceRange(associatedEntity.getModuleName().toSourceText());
}
/**
* Load a class based on the given class name.
* @param className
* @param foreignClassLoader the classloader to use to resolve foreign classes.
* @param recordLoadName the name of the record being loaded.
* This will be used if an error occurs finding the java class.
* @param associatedEntity the name of the associated CAL entity. Can be null an associated entity cannot be provided.
* @param messageHandler the message handler for handling deserialization messages.
* @return the class corresponding to the given name, or null if there is an error in class resolution.
*/
static final Class<?> classForName (final String className, final ClassLoader foreignClassLoader, final String recordLoadName, final CompilerMessage.Identifier associatedEntity, final ForeignEntityProvider.MessageHandler messageHandler) throws UnableToResolveForeignEntityException {
final Class<?> primitiveClass = nameToClassMap.get(className);
if (primitiveClass != null) {
return primitiveClass;
}
final ForeignEntityResolver.ResolutionResult<Class<?>> classResolution = ForeignEntityResolver.resolveClass(ForeignEntityResolver.javaSourceReferenceNameToJvmInternalName(className), foreignClassLoader);
final ForeignEntityResolver.ResolutionStatus resolutionStatus = classResolution.getStatus();
final Class<?> foreignType = classResolution.getResolvedEntity();
if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.SUCCESS) {
// the resolution was successful, so no need to report errors
} else {
final SourceRange sourceRangeForAssociatedEntity = getSourceRangeForAssociatedEntity(associatedEntity);
Throwable throwable = classResolution.getThrowable();
Exception exception = throwable instanceof Exception ? (Exception)throwable : null;
if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.NO_SUCH_ENTITY) {
// The Java class {qualifiedClassName} was not found while loading {recordLoadName}.
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.JavaClassNotFoundWhileLoading(className, recordLoadName), exception));
} else if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.DEPENDEE_CLASS_NOT_FOUND) {
// The Java class {notFoundClass} was not found. This class is required by {externalName}.
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.DependeeJavaClassNotFound(classResolution.getAssociatedMessage(), className), exception));
} else if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.CANNOT_LOAD_CLASS) {
// The definition of Java class {externalName} could not be loaded.
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.JavaClassDefinitionCouldNotBeLoaded(className), exception));
} else if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.CANNOT_INITIALIZE_CLASS) {
// The Java class {qualifiedClassName} could not be initialized.
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.JavaClassCouldNotBeInitialized(className), exception));
} else if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.LINKAGE_ERROR) {
// The java class {qualifiedClassName} was found, but there were problems with using it.
// Class: {LinkageError.class}
// Message: {e.getMessage()}
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.ProblemsUsingJavaClass(className, (LinkageError)throwable)));
} else if (resolutionStatus == ForeignEntityResolver.ResolutionStatus.NOT_ACCESSIBLE) {
//"The Java type ''{0}'' is not accessible. It does not have public scope or is in an unnamed package."
//the parameter {0} will be replaced by "class java.lang.Foo" or "interface java.lang.Foo" as appropriate.
messageHandler.handleMessage(new CompilerMessage(
sourceRangeForAssociatedEntity,
associatedEntity,
new MessageKind.Error.ExternalClassNotAccessible(foreignType), exception));
} else {
// Some other unexpected status
throw new IllegalStateException("Unexpected status: " + resolutionStatus);
}
}
return foreignType;
}
}