/*
* JSwiff is an open source Java API for Macromedia Flash file generation
* and manipulation
*
* Copyright (C) 2004-2005 Ralf Terdic (contact@jswiff.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.jswiff.swfrecords.actions;
import com.jswiff.io.InputBitStream;
import com.jswiff.io.OutputBitStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* <p>
* This action defines a function.
* </p>
*
* <p>
* Note: DefineFunction is rarely used as of SWF 7 and later; it has been
* superseded by DefineFunction2.
* </p>
*
* <p>
* Performed stack operations:
*
* <ul>
* <li>
* standard function declarations do not touch the stack
* </li>
* <li>
* when a function name is not specified, it is assumed that a function literal
* (anonymous function) is declared. In this case, the declared function is
* pushed to the stack so it can either be assigned or invoked:<br>
* </li>
* </ul>
* </p>
*
* <p>
* Note: Use <code>Return</code> to declare the function's result. Otherwise
* the function has no result, and <code>undefined</code> is pushed to stack
* upon invocation.
* </p>
*
* <p>
* ActionScript equivalents:
*
* <ul>
* <li>
* standard function declaration, e.g.<br>
* <code>myFunction(x) {<br>
* return (x + 3);<br>}</code><br>
* </li>
* <li>
* anonymous function declaration, e.g.<br>
* <code>function (x) { x + 3 };</code><br>
* </li>
* <li>
* anonymous function invocation, e.g.<br>
* <code>function (x) { x + 3 } (1);</code><br>
* </li>
* <li>
* method declaration
* </li>
* </ul>
* </p>
*
* @see DefineFunction2
* @since SWF 5
*/
public final class DefineFunction extends Action {
private String name;
private String[] parameters;
private ActionBlockReader body;
/**
* Creates a new DefineFunction action. Use the empty string ("") as function
* name for anonymous functions.
*
* @param functionName name of the function
* @param parameters array of parameter names
*/
public DefineFunction(String functionName, String[] parameters) {
code = ActionConstants.DEFINE_FUNCTION;
this.name = functionName;
this.parameters = parameters;
body = ActionBlock.getInstance();
}
/*
* Creates a new DefineFunction action. Data is read from a bit stream.
*/
DefineFunction(InputBitStream stream, InputBitStream mainStream)
throws IOException {
code = ActionConstants.DEFINE_FUNCTION;
name = stream.readString();
int numParams = stream.readUI16();
if (numParams >= 0) {
parameters = new String[numParams];
for (int i = 0; i < numParams; i++) {
parameters[i] = stream.readString();
}
}
int codeSize = stream.readUI16();
// now read further actions from the main stream
// read action block
byte[] blockBuffer = mainStream.readBytes(codeSize);
InputBitStream blockStream = new InputBitStream(blockBuffer);
blockStream.setANSI(stream.isANSI());
blockStream.setShiftJIS(stream.isShiftJIS());
// body = new ActionBlock(blockStream);
body = ActionBlock.getInstance();
body.read(blockStream);
}
/**
* Returns an <code>ActionBlock</code> containing the function's body. Use
* <code>addAction()</code> to add actions to the body.
*
* @return function body as action block
*/
public ActionBlockReader getBody() {
return body;
}
/**
* Returns the name of the function (or the empty string for anonymous
* functions).
*
* @return function name
*/
public String getName() {
return name;
}
/**
* Returns the names of the function parameters.
*
* @return array of parameter names
*/
public String[] getParameters() {
return parameters;
}
/**
* Returns the size of this action record in bytes.
*
* @return size of this record
*
* @see Action#getSize()
*/
public int getSize() {
int size = 8 + body.getSize();
try {
// function name (unicode, not null-terminated)
// (+ parameters.length: for each param a terminating 0)
int paramLength = (parameters == null) ? 0 : parameters.length;
size += (name.getBytes("UTF-8").length + paramLength);
for (int i = 0; i < paramLength; i++) {
size += parameters[i].getBytes("UTF-8").length; // unicode, null-terminated
}
} catch (UnsupportedEncodingException e) {
// UTF-8 should be available
}
return size;
}
/**
* Adds an action record to the function body.
*
* @param action action record
*/
public void addAction(Action action) {
body.addAction(action);
}
/**
* Returns a short description of this action.
*
* @return <code>"DefineFunction"</code>, function name
*/
public String toString() {
return "DefineFunction " + name;
}
protected void writeData(
OutputBitStream dataStream, OutputBitStream mainStream)
throws IOException {
dataStream.writeString(name);
dataStream.writeUI16((parameters == null) ? 0 : parameters.length);
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
dataStream.writeString(parameters[i]);
}
}
dataStream.writeUI16(body.getSize()); // codeSize
body.write(mainStream, false);
}
}