blob: caf7a81f5e82422f4c9905761042206123745b32 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.royale.compiler.internal.embedding.transcoders;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.config.CompilerDiagnosticsConstants;
import org.apache.royale.compiler.constants.INamespaceConstants;
import org.apache.royale.compiler.embedding.transcoders.ITranscoder;
import org.apache.royale.compiler.filespecs.IBinaryFileSpecification;
import org.apache.royale.compiler.embedding.EmbedAttribute;
import org.apache.royale.compiler.internal.embedding.EmbedData;
import org.apache.royale.compiler.internal.embedding.EmbedMIMEType;
import org.apache.royale.compiler.internal.semantics.PostProcessStep;
import org.apache.royale.compiler.internal.tree.as.ClassNode;
import org.apache.royale.compiler.internal.tree.as.FileNode;
import org.apache.royale.compiler.internal.tree.as.IdentifierNode;
import org.apache.royale.compiler.internal.tree.as.ImportNode;
import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode;
import org.apache.royale.compiler.internal.tree.as.PackageNode;
import org.apache.royale.compiler.internal.tree.as.ScopedBlockNode;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.problems.EmbedUnableToReadSourceProblem;
import org.apache.royale.compiler.problems.EmbedUnsupportedAttributeProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.swc.ISWCFileEntry;
import org.apache.royale.swf.tags.DefineBinaryDataTag;
import org.apache.royale.swf.tags.ICharacterTag;
import org.apache.royale.swf.tags.ITag;
/**
* Base class for all embed transcoders
*/
public abstract class TranscoderBase implements ITranscoder
{
public static final String CORE_PACKAGE = "mx.core";
protected static final String UTILS_PACKAGE = "mx.utils";
protected static final String byteArrayNamePostfix = "ByteArray";
/**
* @param data
* @param workspace
*/
protected TranscoderBase(EmbedData data, Workspace workspace)
{
this.data = data;
this.workspace = workspace;
this.swcSource = data.getSWCSource();
}
protected final EmbedData data;
protected final Workspace workspace;
protected final ISWCFileEntry swcSource;
protected String baseClassQName;
protected String source;
private EmbedMIMEType mimeType;
/**
* Transcode the embedded asset
* @param tags The collection of transcoded asset tags
* @param problems The collection of compiler problems to which this method will add problems.
* @return map of symbol name to character asset tags. null if error
*/
protected abstract Map<String, ICharacterTag> doTranscode(Collection<ITag> tags, Collection<ICompilerProblem> problems);
/**
* The relevant SWF tags
* @param tags The transcoded asset tags
* @param problems The collection of compiler problems to which this method will add problems.
* @return map of symbol name to character asset tags. null if error. the
* returned map may not be modified.
*/
public Map<String, ICharacterTag> getTags(Collection<ITag> tags, Collection<ICompilerProblem> problems)
{
return doTranscode(tags, problems);
}
public String getBaseClassQName()
{
return baseClassQName;
}
/**
* @return The name of the base class of the generated class
*/
public String getBaseClassName()
{
return baseClassQName.substring(baseClassQName.lastIndexOf(".") + 1);
}
/**
* Analyze the attributes
* @param location Source location from where the embed came from
* @param problems Any problems discovered in the EmbedNode
* @return false if analyze failed
*/
public boolean analyze(ISourceLocation location, Collection<ICompilerProblem> problems)
{
boolean result = true;
for (EmbedAttribute attribute : data.getAttributes())
{
if (!setAttribute(attribute))
{
problems.add(new EmbedUnsupportedAttributeProblem(location, attribute, (EmbedMIMEType)data.getAttribute(EmbedAttribute.MIME_TYPE)));
result = false;
}
}
if (!checkAttributeValues(location, problems))
result = false;
return result;
}
/**
* Stores the supported attribute of the transcoder
* @param attribute
* @return false if a non-supported attribute
*/
protected boolean setAttribute(EmbedAttribute attribute)
{
boolean isSupported = true;
switch (attribute)
{
case SOURCE:
source = (String)data.getAttribute(EmbedAttribute.SOURCE);
break;
case MIME_TYPE:
mimeType = (EmbedMIMEType)data.getAttribute(EmbedAttribute.MIME_TYPE);
break;
default:
isSupported = false;
}
return isSupported;
}
/**
* Verify the attributes are valid for the specific transcoder
* @param location
* @param problems
* @return false if contains invalid attributes
*/
protected boolean checkAttributeValues(ISourceLocation location, Collection<ICompilerProblem> problems)
{
return true;
}
protected DefineBinaryDataTag buildBinaryDataTag(Collection<ICompilerProblem> problems)
{
byte[] bytes = getDataBytes(problems);
if (bytes == null)
return null;
return new DefineBinaryDataTag(bytes);
}
/**
* Get the input stream of the embedded asset
*
* @param problems The collection of compiler problems to which this method will add problems.
* @return resultant stream. null on error
*/
protected InputStream getDataStream(Collection<ICompilerProblem> problems)
{
InputStream inStrm = null;
if (swcSource != null)
{
inStrm = getDataStream(swcSource, problems);
}
else
{
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("TranscodeBase waiting for lock in getLatestBinaryFileSpecification");
IBinaryFileSpecification fileSpec = workspace.getLatestBinaryFileSpecification(source);
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("TranscodeBase done with lock in getLatestBinaryFileSpecification");
inStrm = getDataStream(fileSpec, problems);
}
return inStrm;
}
private InputStream getDataStream(ISWCFileEntry swcSource, Collection<ICompilerProblem> problems)
{
InputStream inStrm = null;
try
{
inStrm = swcSource.createInputStream();
}
catch (IOException e)
{
problems.add(new EmbedUnableToReadSourceProblem(e, swcSource.getPath()));
}
return inStrm;
}
private InputStream getDataStream(IBinaryFileSpecification fileSpec, Collection<ICompilerProblem> problems)
{
InputStream inStrm = null;
try
{
inStrm = fileSpec.createInputStream();
}
catch (IOException e)
{
problems.add(new EmbedUnableToReadSourceProblem(e, fileSpec.getPath()));
}
return inStrm;
}
/**
* Read the original bytes of the embedded asset
*
* @param problems
* @return resultant bytes
*/
protected byte[] getDataBytes(Collection<ICompilerProblem> problems)
{
InputStream inStrm = getDataStream(problems);
if (inStrm == null)
return null;
byte[] bytes = null;
try
{
bytes = IOUtils.toByteArray(inStrm);
}
catch (IOException e)
{
problems.add(new EmbedUnableToReadSourceProblem(e, source));
}
finally
{
if (inStrm != null)
{
try
{
inStrm.close();
}
catch (IOException e)
{
// don't care. error case anyway...
}
}
}
return bytes;
}
/**
* Build an AST to represent the embedded asset class
*
* @param problems The collection of compiler problems to which this method will add problems.
* @param filename The path to the file being embedded.
* @return generated class AST
*/
public FileNode buildAST(Collection<ICompilerProblem> problems, String filename)
{
FileNode fileNode = new FileNode(workspace, filename);
PackageNode packageNode = new PackageNode(new IdentifierNode(""), null);
fileNode.addItem(packageNode);
ScopedBlockNode contents = packageNode.getScopedNode();
ImportNode importNode = ImportNode.buildImportNode(getBaseClassQName());
contents.addItem(importNode);
ClassNode classNode = new ClassNode(new IdentifierNode(data.getQName()));
classNode.setBaseClass(new IdentifierNode(getBaseClassName()));
classNode.setNamespace(new NamespaceIdentifierNode(INamespaceConstants.public_));
contents.addItem(classNode);
fileNode.runPostProcess(EnumSet.of(PostProcessStep.POPULATE_SCOPE));
return fileNode;
}
/**
* Build ABC to represent the embedded asset class
*
* @param project The compiler project.
* @param problems The collecton of compiler problems to which this method will add problems.
* @return generated class ABC
*/
public byte[] buildABC(ICompilerProject project, Collection<ICompilerProblem> problems)
{
assert false : "unimplemented buildABC called";
return null;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof TranscoderBase))
return false;
TranscoderBase t = (TranscoderBase)o;
if (!source.equals(t.source) ||
!baseClassQName.equals(t.baseClassQName) ||
mimeType != t.mimeType)
{
return false;
}
if ((swcSource == null) && (t.swcSource != null) ||
(swcSource != null) && (t.swcSource == null))
{
return false;
}
if (swcSource != null)
{
if (!swcSource.getContainingSWCPath().equals(t.swcSource.getContainingSWCPath()) ||
!swcSource.getPath().equals(t.swcSource.getPath()))
{
return false;
}
}
return true;
}
@Override
public int hashCode()
{
int hashCode = 0;
hashCode = source.hashCode();
hashCode ^= baseClassQName.hashCode();
hashCode ^= mimeType.toString().hashCode(); // the toString() gives us a reproducible hash code
// if there is a swcSource, just use the two paths, and
// ignore mod time, as we don't take that into account
// for regular files
if (swcSource != null)
{
hashCode ^= swcSource.getContainingSWCPath().hashCode();
hashCode ^= swcSource.getPath().hashCode();
}
return hashCode;
}
}