private Node createStubCall(Node originalDefinition, int stubId) {
        return astFactory.createCall(// We can't look up the type of the stub creating method, because we add its
        // definition after type checking.
        astFactory.createNameWithUnknownType(STUB_METHOD_NAME), type(originalDefinition), astFactory.createNumber(stubId)).srcrefTreeIfMissing(originalDefinition);
    }

package com.google.javascript.jscomp;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.javascript.jscomp.AstFactory.type;
import com.google.common.annotations.VisibleForTesting;
import com.google.javascript.jscomp.AnalyzePrototypeProperties.ClassMemberFunction;
import com.google.javascript.jscomp.AnalyzePrototypeProperties.NameInfo;
import com.google.javascript.jscomp.AnalyzePrototypeProperties.Property;
import com.google.javascript.jscomp.AnalyzePrototypeProperties.PrototypeProperty;
import com.google.javascript.jscomp.AnalyzePrototypeProperties.Symbol;
import com.google.javascript.rhino.Node;
import java.util.Collection;
import java.util.Iterator;

/**
 * Move prototype methods into later chunks.
 */
public class CrossChunkMethodMotion implements CompilerPass {

    // Internal errors
    static final DiagnosticType NULL_COMMON_MODULE_ERROR = DiagnosticType.error("JSC_INTERNAL_ERROR_MODULE_DEPEND", "null deepest common module");

    private final AbstractCompiler compiler;

    private final IdGenerator idGenerator;

    private final AnalyzePrototypeProperties analyzer;

    private final JSChunkGraph moduleGraph;

    private final boolean noStubFunctions;

    private final AstFactory astFactory;

    static final String STUB_METHOD_NAME = "JSCompiler_stubMethod";

    static final String UNSTUB_METHOD_NAME = "JSCompiler_unstubMethod";

    @VisibleForTesting
    public static final String STUB_DECLARATIONS = "var JSCompiler_stubMap = [];" + "function JSCompiler_stubMethod(JSCompiler_stubMethod_id) {" + "  return function() {" + "    return JSCompiler_stubMap[JSCompiler_stubMethod_id].apply(" + "        this, arguments);" + "  };" + "}" + "function JSCompiler_unstubMethod(" + "    JSCompiler_unstubMethod_id, JSCompiler_unstubMethod_body) {" + "  return JSCompiler_stubMap[JSCompiler_unstubMethod_id] = " + "      JSCompiler_unstubMethod_body;" + "}";

    /**
     * Creates a new pass for moving prototype properties.
     *
     * @param compiler The compiler.
     * @param idGenerator An id generator for method stubs.
     * @param canModifyExterns If true, then we can move prototype properties that are declared in the
     *     externs file.
     * @param noStubFunctions if true, we can move methods without stub functions in the parent
     *     chunk.
     */
    CrossChunkMethodMotion(AbstractCompiler compiler, IdGenerator idGenerator, boolean canModifyExterns, boolean noStubFunctions) {
        this.compiler = compiler;
        this.idGenerator = idGenerator;
        this.moduleGraph = compiler.getModuleGraph();
        this.analyzer = new AnalyzePrototypeProperties(compiler, moduleGraph, canModifyExterns, false, /* anchorUnusedVars */
        noStubFunctions);
        this.noStubFunctions = noStubFunctions;
        this.astFactory = compiler.createAstFactory();
    }

    @Override
    public void process(Node externRoot, Node root) {
        // If there are < 2 chunks, then we will never move anything,
        // so we're done.
        if (moduleGraph.getChunkCount() > 1) {
            analyzer.process(externRoot, root);
            moveMethods(analyzer.getAllNameInfo());
        }
    }

    /**
     * Move methods deeper in the chunk graph when possible.
     */
    private void moveMethods(Collection<NameInfo> allNameInfo) {
        boolean hasStubDeclaration = idGenerator.hasGeneratedAnyIds();
        for (NameInfo nameInfo : allNameInfo) {
            if (!nameInfo.isReferenced()) {
                // The code below can't do anything with unreferenced name
                // infos.  They should be skipped to avoid NPE since their
                continue;
            }
            if (nameInfo.readsClosureVariables()) {
                continue;
            }
            JSChunk deepestCommonModuleRef = nameInfo.getDeepestCommonModuleRef();
            if (deepestCommonModuleRef == null) {
                compiler.report(JSError.make(NULL_COMMON_MODULE_ERROR));
                continue;
            }
            Iterator<Symbol> declarations = nameInfo.getDeclarations().descendingIterator();
            while (declarations.hasNext()) {
                Symbol symbol = declarations.next();
                if (symbol instanceof PrototypeProperty) {
                    tryToMovePrototypeMethod(nameInfo, deepestCommonModuleRef, (PrototypeProperty) symbol);
                } else if (symbol instanceof ClassMemberFunction) {
                    tryToMoveMemberFunction(nameInfo, deepestCommonModuleRef, (ClassMemberFunction) symbol);
                }
                // else it's a variable definition, and we don't move those.
            }
        }
        if (!noStubFunctions && !hasStubDeclaration && idGenerator.hasGeneratedAnyIds()) {
            // Declare stub functions in the top-most chunk.
            Node declarations = compiler.parseSyntheticCode("chunk_method_stubbing", STUB_DECLARATIONS);
            NodeUtil.markNewScopesChanged(declarations, compiler);
            Node firstScript = compiler.getNodeForCodeInsertion(null);
            firstScript.addChildrenToFront(declarations.removeChildren());
            compiler.reportChangeToEnclosingScope(firstScript);
        }
    }

    private void tryToMovePrototypeMethod(NameInfo nameInfo, JSChunk deepestCommonModuleRef, PrototypeProperty prop) {
        // We should only move a property across chunks if:
        // 1) We can move it deeper in the chunk graph, and
        // 2) it's a function, and
        // 3) it is not a GETTER_DEF or a SETTER_DEF, and
        // 4) it does not refer to `super`
        // 5) the class is available in the global scope.
        // #1 should be obvious. #2 is more subtle. It's possible
        // to copy off of a prototype, as in the code:
        // for (var k in Foo.prototype) {
        // This is a common way to implement pseudo-multiple inheritance in JS.
        // So if we move a prototype method into a deeper chunk, we must
        // replace it with a stub function so that it preserves its original
        if (prop.getRootVar() == null || !prop.getRootVar().isGlobal()) {
            return;
        }
        if (nameInfo.referencesSuper()) {
            // It is illegal to move `super` outside of a member function def.
            return;
        }
        Node value = prop.getValue();
        Node valueParent = value.getParent();
        // Only attempt to move normal functions.
        if (!value.isFunction() || // A GET or SET can't be deferred like a normal
        // FUNCTION property definition as a mix-in would get the result
        // of a GET instead of the function itself.
        valueParent.isGetterDef() || valueParent.isSetterDef()) {
            return;
        }
        if (moduleGraph.dependsOn(deepestCommonModuleRef, prop.getModule())) {
            if (hasUnmovableRedeclaration(nameInfo, prop)) {
                // If it has been redeclared on the same object, skip it.
                return;
            }
            Node destParent = compiler.getNodeForCodeInsertion(deepestCommonModuleRef);
            if (valueParent.isMemberFunctionDef()) {
                movePrototypeObjectLiteralMethodShorthand(nameInfo.name, destParent, value);
            } else if (valueParent.isStringKey()) {
                movePrototypeObjectLiteralProperty(nameInfo.name, destParent, value);
            } else {
                // Note that computed properties should have been filtered out by
                // AnalyzePrototypeProperties, because they don't have a recognizable property name.
                // Getters and setters are filtered out by the code above
                checkState(valueParent.isAssign(), valueParent);
                movePrototypeDotMethodAssignment(destParent, value);
            }
        }
    }

    /**
     * Move a property defined by object literal assigned to `.prototype`.
     *
     *

     *     Foo.prototype = { propName: function() {}};
     *

     */
    private void movePrototypeObjectLiteralProperty(String propName, Node destParent, Node functionNode) {
        checkState(functionNode.isFunction(), functionNode);
        Node stringKey = functionNode.getParent();
        checkState(stringKey.isStringKey(), stringKey);
        Node prototypeObjectLiteral = stringKey.getParent();
        checkState(prototypeObjectLiteral.isObjectLit(), prototypeObjectLiteral);
        Node assignNode = prototypeObjectLiteral.getParent();
        checkState(assignNode.isAssign() && prototypeObjectLiteral.isSecondChildOf(assignNode), assignNode);
        Node ownerDotPrototypeNode = assignNode.getFirstChild();
        checkState(ownerDotPrototypeNode.isQualifiedName() && ownerDotPrototypeNode.getString().equals("prototype"), ownerDotPrototypeNode);
        if (noStubFunctions) {
            // Remove the definition from the object literal
            stringKey.detach();
            compiler.reportChangeToEnclosingScope(prototypeObjectLiteral);
            // Prepend definition to new chunk
            Node ownerDotPrototypeDotPropName = astFactory.createGetProp(ownerDotPrototypeNode.cloneTree(), propName, type(functionNode));
            functionNode.detach();
            Node definitionStatement = astFactory.createAssignStatement(ownerDotPrototypeDotPropName, functionNode).srcrefTreeIfMissing(stringKey);
            destParent.addChildToFront(definitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        } else {
            int stubId = idGenerator.newId();
            Node stubCall = createStubCall(functionNode, stubId);
            functionNode.replaceWith(stubCall);
            compiler.reportChangeToEnclosingScope(prototypeObjectLiteral);
            // Prepend definition to new chunk
            Node ownerDotPrototypeDotPropName = astFactory.createGetProp(ownerDotPrototypeNode.cloneTree(), propName, type(functionNode));
            Node unstubCall = createUnstubCall(functionNode, stubId);
            Node definitionStatement = astFactory.createAssignStatement(ownerDotPrototypeDotPropName, unstubCall).srcrefTreeIfMissing(stringKey);
            destParent.addChildToFront(definitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        }
    }

    /**
     * Move a property defined by assignment to `.prototype` or `.prototype.propName`.
     *
     *

     *     Foo.prototype.propName = function() {};
     *

     */
    private void movePrototypeDotMethodAssignment(Node destParent, Node functionNode) {
        checkState(functionNode.isFunction(), functionNode);
        Node assignNode = functionNode.getParent();
        checkState(assignNode.isAssign() && functionNode.isSecondChildOf(assignNode), assignNode);
        Node definitionStatement = assignNode.getParent();
        checkState(definitionStatement.isExprResult(), assignNode);
        if (noStubFunctions) {
            // Remove the definition statement from its current location
            Node assignStatementParent = definitionStatement.getParent();
            definitionStatement.detach();
            compiler.reportChangeToEnclosingScope(assignStatementParent);
            // Prepend definition to new chunk
            destParent.addChildToFront(definitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        } else {
            int stubId = idGenerator.newId();
            // replace function definition with temporary placeholder so we can clone the whole
            // assignment statement without cloning the function definition itself.
            Node originalDefinitionPlaceholder = astFactory.createEmpty();
            functionNode.replaceWith(originalDefinitionPlaceholder);
            Node newDefinitionStatement = definitionStatement.cloneTree();
            Node newDefinitionPlaceholder = // EXPR_RESULT
            newDefinitionStatement.// ASSIGN
            getOnlyChild().// EMPTY RHS node
            getLastChild();
            // convert original assignment statement to
            Node stubCall = createStubCall(functionNode, stubId);
            originalDefinitionPlaceholder.replaceWith(stubCall);
            compiler.reportChangeToEnclosingScope(definitionStatement);
            // Prepend new definition to new chunk
            Node unstubCall = createUnstubCall(functionNode, stubId);
            newDefinitionPlaceholder.replaceWith(unstubCall);
            destParent.addChildToFront(newDefinitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        }
    }

    /**
     * Move a property defined by assignment to `.prototype` or `.prototype.propName`.
     *
     *

     *     Foo.prototype = { propName() {}};
     *

     */
    private void movePrototypeObjectLiteralMethodShorthand(String propName, Node destParent, Node functionNode) {
        checkState(functionNode.isFunction(), functionNode);
        Node memberFunctionDef = functionNode.getParent();
        checkState(memberFunctionDef.isMemberFunctionDef(), memberFunctionDef);
        Node prototypeObjectLiteral = memberFunctionDef.getParent();
        checkState(prototypeObjectLiteral.isObjectLit(), prototypeObjectLiteral);
        Node assignNode = prototypeObjectLiteral.getParent();
        checkState(assignNode.isAssign() && prototypeObjectLiteral.isSecondChildOf(assignNode), assignNode);
        Node ownerDotPrototypeNode = assignNode.getFirstChild();
        checkState(ownerDotPrototypeNode.isQualifiedName() && ownerDotPrototypeNode.getString().equals("prototype"), ownerDotPrototypeNode);
        if (noStubFunctions) {
            // Remove the definition from the object literal
            memberFunctionDef.detach();
            compiler.reportChangeToEnclosingScope(prototypeObjectLiteral);
            // Prepend definition to new chunk
            Node ownerDotPrototypeDotPropName = astFactory.createGetProp(ownerDotPrototypeNode.cloneTree(), propName, type(functionNode));
            Node definitionStatement = astFactory.createAssignStatement(ownerDotPrototypeDotPropName, functionNode.detach()).srcrefTreeIfMissing(memberFunctionDef);
            destParent.addChildToFront(definitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        } else {
            int stubId = idGenerator.newId();
            Node stubCall = createStubCall(functionNode, stubId);
            memberFunctionDef.replaceWith(astFactory.createStringKey(propName, stubCall));
            compiler.reportChangeToEnclosingScope(prototypeObjectLiteral);
            // Prepend definition to new chunk
            Node ownerDotPrototypeDotPropName = astFactory.createGetProp(ownerDotPrototypeNode.cloneTree(), propName, type(functionNode));
            Node unstubCall = createUnstubCall(functionNode.detach(), stubId);
            Node definitionStatement = astFactory.createAssignStatement(ownerDotPrototypeDotPropName, unstubCall).srcrefTreeIfMissing(memberFunctionDef);
            destParent.addChildToFront(definitionStatement);
            compiler.reportChangeToEnclosingScope(destParent);
        }
    }

    /**
     * Returns a new Node to be used as the stub definition for a method.
     *
     * @param originalDefinition function Node whose definition is being stubbed
     * @param stubId ID to use for stubbing and unstubbing
     * @return a Node that looks like JSCompiler_stubMethod(0)
     */

/**
     * Returns a new Node to be used as the stub definition for a method.
     *
     * @param functionNode actual function definition to be attached. Must be detached now.
     * @param stubId ID to use for stubbing and unstubbing
     * @return a Node that looks like JSCompiler_unstubMethod(0, function() {})
     */
    private Node createUnstubCall(Node functionNode, int stubId) {
        return astFactory.createCall(// We can't look up the type of the stub creating method, because we add its
        // definition after type checking.
        astFactory.createNameWithUnknownType(UNSTUB_METHOD_NAME), type(functionNode), astFactory.createNumber(stubId), functionNode).srcrefTreeIfMissing(functionNode);
    }

    /**
     * If possible, move a class instance member function definition to the deepest chunk common to
     * all uses of the method.
     *
     * @param nameInfo information about all definitions of the given property name
     * @param deepestCommonModuleRef all uses of the method are either in this chunk or in chunks that
     *     depend on it
     * @param classMemberFunction definition of the method within its class body
     */
    private void tryToMoveMemberFunction(NameInfo nameInfo, JSChunk deepestCommonModuleRef, ClassMemberFunction classMemberFunction) {
        // We should only move a property across chunks if:
        // 1) We can move it deeper in the chunk graph,
        // 2) and it's a normal member function, and not a GETTER_DEF or a SETTER_DEF, or
        // 3) and it does not refer to `super`, which would be invalid outside of the class.
        // 4) and the class is available in the global scope.
        Var rootVar = classMemberFunction.getRootVar();
        if (rootVar == null || !rootVar.isGlobal()) {
            return;
        }
        Node definitionNode = classMemberFunction.getDefinitionNode();
        // Only attempt to move normal member functions.
        // A getter or setter cannot be as easily defined outside of the class to which it belongs.
        if (!definitionNode.isMemberFunctionDef()) {
            return;
        }
        if (NodeUtil.isEs6ConstructorMemberFunctionDef(definitionNode)) {
            // Constructor functions cannot be moved.
            return;
        }
        if (nameInfo.referencesSuper()) {
            // Do not move methods containing `super`, because it doesn't work outside of a
            // class method or object literal method.
            return;
        }
        if (moduleGraph.dependsOn(deepestCommonModuleRef, classMemberFunction.getModule())) {
            if (hasUnmovableRedeclaration(nameInfo, classMemberFunction)) {
                // If it has been redeclared on the same object, skip it.
                return;
            }
            Node destinationParent = compiler.getNodeForCodeInsertion(deepestCommonModuleRef);
            String className = rootVar.getName();
            if (noStubFunctions) {
                moveClassInstanceMethodWithoutStub(className, definitionNode, destinationParent);
            } else {
                moveClassInstanceMethodWithStub(className, definitionNode, destinationParent);
            }
        }
    }

    private void moveClassInstanceMethodWithoutStub(String className, Node methodDefinition, Node destinationParent) {
        checkArgument(methodDefinition.isMemberFunctionDef(), methodDefinition);
        Node classMembers = checkNotNull(methodDefinition.getParent());
        checkState(classMembers.isClassMembers(), classMembers);
        Node classNode = classMembers.getParent();
        checkState(classNode.isClass(), classNode);
        methodDefinition.detach();
        compiler.reportChangeToEnclosingScope(classMembers);
        Node functionNode = checkNotNull(methodDefinition.getOnlyChild());
        Node classNameDotPrototypeDotPropName = astFactory.createGetProp(astFactory.createPrototypeAccess(astFactory.createName(className, type(classNode))), methodDefinition.getString(), type(functionNode));
        functionNode.detach();
        Node definitionStatementNode = astFactory.createAssignStatement(classNameDotPrototypeDotPropName, functionNode).srcrefTreeIfMissing(methodDefinition);
        destinationParent.addChildToFront(definitionStatementNode);
        compiler.reportChangeToEnclosingScope(destinationParent);
    }

    private void moveClassInstanceMethodWithStub(String className, Node methodDefinition, Node destinationParent) {
        checkArgument(methodDefinition.isMemberFunctionDef(), methodDefinition);
        Node classMembers = checkNotNull(methodDefinition.getParent());
        checkState(classMembers.isClassMembers(), classMembers);
        Node classNode = classMembers.getParent();
        checkState(classNode.isClass(), classNode);
        int stubId = idGenerator.newId();
        // Put a stub definition after the class
        Node classNameDotPrototypeDotPropName = astFactory.createGetProp(astFactory.createPrototypeAccess(astFactory.createName(className, type(classNode))), methodDefinition.getString(), type(methodDefinition));
        Node stubCall = createStubCall(methodDefinition, stubId);
        Node stubDefinitionStatement = astFactory.createAssignStatement(classNameDotPrototypeDotPropName, stubCall).srcrefTreeIfMissing(methodDefinition);
        Node classDefiningStatement = NodeUtil.getEnclosingStatement(classMembers);
        stubDefinitionStatement.insertAfter(classDefiningStatement);
        // remove the definition from the class
        methodDefinition.detach();
        compiler.reportChangeToEnclosingScope(classMembers);
        // Prepend unstub definition to the new location.
        Node classNameDotPrototypeDotPropName2 = classNameDotPrototypeDotPropName.cloneTree();
        Node functionNode = checkNotNull(methodDefinition.getOnlyChild());
        functionNode.detach();
        Node unstubCall = createUnstubCall(functionNode, stubId);
        Node statementNode = astFactory.createAssignStatement(classNameDotPrototypeDotPropName2, unstubCall).srcrefTreeIfMissing(methodDefinition);
        destinationParent.addChildToFront(statementNode);
        compiler.reportChangeToEnclosingScope(destinationParent);
    }

    static boolean hasUnmovableRedeclaration(NameInfo nameInfo, Property prop) {
        for (Symbol symbol : nameInfo.getDeclarations()) {
            if (symbol instanceof Property) {
                Property otherProp = (Property) symbol;
                // It is possible to do better here if the dependencies are well defined
                // but redefinitions are usually in optional chunks so it isn't likely
                // worth the effort to check.
                if (prop != otherProp && prop.getRootVar() == otherProp.getRootVar() && prop.getModule() != otherProp.getModule()) {
                    return true;
                }
            }
        }
        return false;
    }
}


while (left < S.length() && !isAbc(chars[left])) left++;

class Solution {

    public String reverseOnlyLetters(String S) {
        int left = 0, right = S.length() - 1;
        char[] chars = S.toCharArray();
        while (left < right) {

            while (right >= 0 && !isAbc(chars[right])) right--;
            if (left >= right)
                break;
            char tmp = chars[left];
            chars[left++] = chars[right];
            chars[right--] = tmp;
        }
        return String.valueOf(chars);
    }

    public boolean isAbc(char ch) {
        if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        return false;
    }
}


@Override
    protected void initTypeAndSize() {
        elementSize = 2;
        type = DataType.SHORT;
    }

package org.nd4j.linalg.jcublas.buffer;

import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.indexer.Indexer;
import org.nd4j.linalg.api.buffer.DataBuffer;
import org.nd4j.linalg.api.buffer.DataType;
import org.nd4j.linalg.api.memory.MemoryWorkspace;
import org.nd4j.common.util.ArrayUtil;
import java.nio.ByteBuffer;

/**
 * Cuda Short buffer
 *
 * @author example@nnthink.com
 */
public class CudaShortDataBuffer extends BaseCudaDataBuffer {

    /**
     * Meant for creating another view of a buffer
     *
     * @param pointer the underlying buffer to create a view from
     * @param indexer the indexer for the pointer
     * @param length  the length of the view
     */
    public CudaShortDataBuffer(Pointer pointer, Indexer indexer, long length) {
        super(pointer, indexer, length);
    }

    public CudaShortDataBuffer(Pointer pointer, Pointer specialPointer, Indexer indexer, long length) {
        super(pointer, specialPointer, indexer, length);
    }

    public CudaShortDataBuffer(ByteBuffer buffer, DataType dataType, long length, long offset) {
        super(buffer, dataType, length, offset);
    }

    /**
     * Base constructor
     *
     * @param length the length of the buffer
     */
    public CudaShortDataBuffer(long length) {
        super(length, 2);
    }

    public CudaShortDataBuffer(long length, boolean initialize) {
        super(length, 2, initialize);
    }

    public CudaShortDataBuffer(long length, int elementSize) {
        super(length, elementSize);
    }

    public CudaShortDataBuffer(long length, int elementSize, long offset) {
        super(length, elementSize, offset);
    }

    public CudaShortDataBuffer(long length, boolean initialize, MemoryWorkspace workspace) {
        super(length, 2, initialize, workspace);
    }

    public CudaShortDataBuffer(float[] data, boolean copy, MemoryWorkspace workspace) {
        super(data, copy, 0, workspace);
    }

    /**
     * Initialize the opType of this buffer
     */

    public CudaShortDataBuffer(DataBuffer underlyingBuffer, long length, long offset) {
        super(underlyingBuffer, length, offset);
    }

    public CudaShortDataBuffer(float[] buffer) {
        super(buffer);
    }

    public CudaShortDataBuffer(float[] data, boolean copy) {
        super(data, copy);
    }

    public CudaShortDataBuffer(float[] data, boolean copy, long offset) {
        super(data, copy, offset);
    }

    public CudaShortDataBuffer(float[] data, boolean copy, long offset, MemoryWorkspace workspace) {
        super(data, copy, offset, workspace);
    }

    public CudaShortDataBuffer(double[] data) {
        super(data);
    }

    public CudaShortDataBuffer(double[] data, boolean copy) {
        super(data, copy);
    }

    public CudaShortDataBuffer(double[] data, boolean copy, long offset) {
        super(data, copy, offset);
    }

    public CudaShortDataBuffer(int[] data) {
        super(data);
    }

    public CudaShortDataBuffer(int[] data, boolean copy) {
        super(data, copy);
    }

    public CudaShortDataBuffer(int[] data, boolean copy, long offset) {
        super(data, copy, offset);
    }

    @Override
    protected DataBuffer create(long length) {
        return new CudaShortDataBuffer(length);
    }

    @Override
    public float[] getFloatsAt(long offset, long inc, int length) {
        return super.getFloatsAt(offset, inc, length);
    }

    @Override
    public double[] getDoublesAt(long offset, long inc, int length) {
        return ArrayUtil.toDoubles(getFloatsAt(offset, inc, length));
    }

    @Override
    public void setData(float[] data) {
        setData(ArrayUtil.toShorts(data));
    }

    @Override
    public void setData(int[] data) {
        setData(ArrayUtil.toShorts(data));
    }

    @Override
    public void setData(double[] data) {
        setData(ArrayUtil.toFloats(data));
    }

    @Override
    public DataType dataType() {
        return DataType.SHORT;
    }

    @Override
    public float[] asFloat() {
        return super.asFloat();
    }

    @Override
    public double[] asDouble() {
        return ArrayUtil.toDoubles(asFloat());
    }

    @Override
    public int[] asInt() {
        return ArrayUtil.toInts(asFloat());
    }

    @Override
    public double getDouble(long i) {
        return super.getFloat(i);
    }

    @Override
    public DataBuffer create(double[] data) {
        return new CudaShortDataBuffer(data);
    }

    @Override
    public DataBuffer create(float[] data) {
        return new CudaShortDataBuffer(data);
    }

    @Override
    public DataBuffer create(int[] data) {
        return new CudaShortDataBuffer(data);
    }

    @Override
    public void flush() {
    }
}


for (int i = 0; i < 10000; i++) {
            DownloadData data = new DownloadData();
            data.setString("字符串" + 0);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }

package cn.timebusker.web;

import com.alibaba.excel.EasyExcel;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Controller
public class DownloadExcelDataController {

    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
    }

    private List<DownloadData> data() {
        List<DownloadData> list = new ArrayList<DownloadData>();


for (int i = 0; i < cg.subgraphs.size(); i++) {
            Node node = cg.subgraphs.getNode(i);
            temp = node.width;
            node.width = node.height;
            node.height = temp;
            if (node.getPadding() != null)
                node.setPadding(t.t(node.getPadding()));
        }

/**
 * ****************************************************************************
 *
 *  Contributors:
 *      IBM Corporation - initial API and implementation
 * *****************************************************************************
 */
package org.eclipse.draw2d.graph;

import org.eclipse.draw2d.PositionConstants;

/**
 * Performs transposing of subgraphics in a compound directed graph.
 *
 * @since 3.7.1
 */
class CompoundTransposeMetrics extends TransposeMetrics {

    @Override
    public void visit(DirectedGraph g) {
        if (g.getDirection() == PositionConstants.SOUTH)
            return;
        super.visit(g);
        int temp;
        CompoundDirectedGraph cg = (CompoundDirectedGraph) g;

    }

    @Override
    public void revisit(DirectedGraph g) {
        if (g.getDirection() == PositionConstants.SOUTH)
            return;
        super.revisit(g);
        int temp;
        CompoundDirectedGraph cg = (CompoundDirectedGraph) g;
        for (int i = 0; i < cg.subgraphs.size(); i++) {
            Node node = (Node) cg.subgraphs.get(i);
            temp = node.width;
            node.width = node.height;
            node.height = temp;
            temp = node.y;
            node.y = node.x;
            node.x = temp;
            if (node.getPadding() != null)
                node.setPadding(t.t(node.getPadding()));
        }
    }
}


public synchronized void configure() throws IllegalStateException, IOException {
        super.configure();
    }

package app.com.rtsplibrary.rtsp;

import java.io.IOException;

/**
 * Created by user111 on 2018/3/14.
 */
public class ScreenStream extends VideoStream {

    public ScreenStream() {
        mPacketizer = new H264Packetizer();
    }

    public synchronized void start() throws IllegalStateException, IOException {
        if (!mStreaming) {
            super.start();
        }
    }

    /**
     * Configures the stream. You need to call this before calling {@link #getSessionDescription()} to apply
     * your configuration of the stream.
     */

    @Override
    public String getSessionDescription() throws IllegalStateException {
        return "m=video " + String.valueOf(getDestinationPorts()[0]) + " RTP/AVP 96\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=fmtp:96 packetization-mode=1;profile-level-id=000042" + ";sprop-parameter-sets=" + ";\r\n";
    }
}


@Override
    @SuppressWarnings("unchecked")
    public Enum<?> transform(String value, Field field) throws TransformationException {
        @SuppressWarnings("rawtypes")
        Class<? extends Enum> clazz = (Class<? extends Enum>) field.getType();
        try {
            return Enum.valueOf(clazz, value);
        } catch (Exception e) {
            throw new TransformationException(e);
        }
    }

package tools.config.transformers;

import tools.config.TransformationException;
import java.lang.reflect.Field;

/**
 * Transforms enum string representation to enum. String must match case definition of enum, for instance:
 *

 *

 * enum{
 *  FILE,
 *  URL
 * }
 *

 *

 * will be parsed with string "FILE" but not "file".
 *
 * @author SoulKeeper
 */
public class EnumTransformer implements PropertyTransformer<Enum<?>> {

    /**
     * Shared instance of this transformer. It's thread-safe so no need of multiple instances
     */
    public static final EnumTransformer SHARED_INSTANCE = new EnumTransformer();

    /**
     * Trnasforms string to enum
     *
     * @param value value that will be transformed
     * @param field value will be assigned to this field
     * @return Enum object representing the value
     * @throws TransformationException if somehting went wrong
     */


@VisibleForTesting
    public static void reset() {
        IssueRegistry.reset();
    }

package com.android.tools.lint.checks;

import com.android.annotations.NonNull;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;

/**
 * Registry which provides a list of checks to be performed on an Android project
 */
public class BuiltinIssueRegistry extends IssueRegistry {

    private static final List<Issue> sIssues;

    static final int INITIAL_CAPACITY = 222;

    static {
        List<Issue> issues = new ArrayList<Issue>(INITIAL_CAPACITY);
        issues.add(AccessibilityDetector.ISSUE);
        issues.add(AddJavascriptInterfaceDetector.ISSUE);
        issues.add(AlarmDetector.ISSUE);
        issues.add(AlwaysShowActionDetector.ISSUE);
        issues.add(AnnotationDetector.FLAG_STYLE);
        issues.add(AnnotationDetector.INSIDE_METHOD);
        issues.add(AnnotationDetector.UNIQUE);
        issues.add(ApiDetector.INLINED);
        issues.add(ApiDetector.OVERRIDE);
        issues.add(ApiDetector.UNSUPPORTED);
        issues.add(ApiDetector.UNUSED);
        issues.add(AppCompatCallDetector.ISSUE);
        issues.add(AppCompatResourceDetector.ISSUE);
        issues.add(AppIndexingApiDetector.ISSUE_ERROR);
        issues.add(AppIndexingApiDetector.ISSUE_WARNING);
        issues.add(ArraySizeDetector.INCONSISTENT);
        issues.add(AssertDetector.ISSUE);
        issues.add(ButtonDetector.BACK_BUTTON);
        issues.add(ButtonDetector.CASE);
        issues.add(ButtonDetector.ORDER);
        issues.add(ButtonDetector.STYLE);
        issues.add(ByteOrderMarkDetector.BOM);
        issues.add(CallSuperDetector.ISSUE);
        issues.add(ChildCountDetector.ADAPTER_VIEW_ISSUE);
        issues.add(ChildCountDetector.SCROLLVIEW_ISSUE);
        issues.add(CipherGetInstanceDetector.ISSUE);
        issues.add(CleanupDetector.COMMIT_FRAGMENT);
        issues.add(CleanupDetector.RECYCLE_RESOURCE);
        issues.add(ClickableViewAccessibilityDetector.ISSUE);
        issues.add(CommentDetector.EASTER_EGG);
        issues.add(CommentDetector.STOP_SHIP);
        issues.add(CustomViewDetector.ISSUE);
        issues.add(CutPasteDetector.ISSUE);
        issues.add(DateFormatDetector.DATE_FORMAT);
        issues.add(DeprecationDetector.ISSUE);
        issues.add(DetectMissingPrefix.MISSING_NAMESPACE);
        issues.add(DosLineEndingDetector.ISSUE);
        issues.add(DuplicateIdDetector.CROSS_LAYOUT);
        issues.add(DuplicateIdDetector.WITHIN_LAYOUT);
        issues.add(DuplicateResourceDetector.ISSUE);
        issues.add(DuplicateResourceDetector.TYPE_MISMATCH);
        issues.add(ExtraTextDetector.ISSUE);
        issues.add(FieldGetterDetector.ISSUE);
        issues.add(FullBackupContentDetector.ISSUE);
        issues.add(FragmentDetector.ISSUE);
        issues.add(GetSignaturesDetector.ISSUE);
        issues.add(GradleDetector.COMPATIBILITY);
        issues.add(GradleDetector.GRADLE_PLUGIN_COMPATIBILITY);
        issues.add(GradleDetector.DEPENDENCY);
        issues.add(GradleDetector.DEPRECATED);
        issues.add(GradleDetector.GRADLE_GETTER);
        issues.add(GradleDetector.IDE_SUPPORT);
        issues.add(GradleDetector.PATH);
        issues.add(GradleDetector.PLUS);
        issues.add(GradleDetector.STRING_INTEGER);
        issues.add(GradleDetector.REMOTE_VERSION);
        issues.add(GradleDetector.ACCIDENTAL_OCTAL);
        issues.add(GridLayoutDetector.ISSUE);
        issues.add(HandlerDetector.ISSUE);
        issues.add(HardcodedDebugModeDetector.ISSUE);
        issues.add(HardcodedValuesDetector.ISSUE);
        issues.add(IconDetector.DUPLICATES_CONFIGURATIONS);
        issues.add(IconDetector.DUPLICATES_NAMES);
        issues.add(IconDetector.GIF_USAGE);
        issues.add(IconDetector.ICON_COLORS);
        issues.add(IconDetector.ICON_DENSITIES);
        issues.add(IconDetector.ICON_DIP_SIZE);
        issues.add(IconDetector.ICON_EXPECTED_SIZE);
        issues.add(IconDetector.ICON_EXTENSION);
        issues.add(IconDetector.ICON_LAUNCHER_SHAPE);
        issues.add(IconDetector.ICON_LOCATION);
        issues.add(IconDetector.ICON_MISSING_FOLDER);
        issues.add(IconDetector.ICON_MIX_9PNG);
        issues.add(IconDetector.ICON_NODPI);
        issues.add(IconDetector.ICON_XML_AND_PNG);
        issues.add(IncludeDetector.ISSUE);
        issues.add(InefficientWeightDetector.BASELINE_WEIGHTS);
        issues.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
        issues.add(InefficientWeightDetector.NESTED_WEIGHTS);
        issues.add(InefficientWeightDetector.ORIENTATION);
        issues.add(InefficientWeightDetector.WRONG_0DP);
        issues.add(InvalidPackageDetector.ISSUE);
        issues.add(JavaPerformanceDetector.PAINT_ALLOC);
        issues.add(JavaPerformanceDetector.USE_SPARSE_ARRAY);
        issues.add(JavaPerformanceDetector.USE_VALUE_OF);
        issues.add(JavaScriptInterfaceDetector.ISSUE);
        issues.add(LabelForDetector.ISSUE);
        issues.add(LayoutConsistencyDetector.INCONSISTENT_IDS);
        issues.add(LayoutInflationDetector.ISSUE);
        issues.add(LocaleDetector.STRING_LOCALE);
        issues.add(LocaleFolderDetector.DEPRECATED_CODE);
        issues.add(LocaleFolderDetector.INVALID_FOLDER);
        issues.add(LocaleFolderDetector.WRONG_REGION);
        issues.add(LocaleFolderDetector.USE_ALPHA_2);
        issues.add(LogDetector.CONDITIONAL);
        issues.add(LogDetector.LONG_TAG);
        issues.add(LogDetector.WRONG_TAG);
        issues.add(ManifestDetector.ALLOW_BACKUP);
        issues.add(ManifestDetector.APPLICATION_ICON);
        issues.add(ManifestDetector.DEVICE_ADMIN);
        issues.add(ManifestDetector.DUPLICATE_ACTIVITY);
        issues.add(ManifestDetector.DUPLICATE_USES_FEATURE);
        issues.add(ManifestDetector.GRADLE_OVERRIDES);
        issues.add(ManifestDetector.ILLEGAL_REFERENCE);
        issues.add(ManifestDetector.MIPMAP);
        issues.add(ManifestDetector.MOCK_LOCATION);
        issues.add(ManifestDetector.MULTIPLE_USES_SDK);
        issues.add(ManifestDetector.ORDER);
        issues.add(ManifestDetector.SET_VERSION);
        issues.add(ManifestDetector.TARGET_NEWER);
        issues.add(ManifestDetector.UNIQUE_PERMISSION);
        issues.add(ManifestDetector.USES_SDK);
        issues.add(ManifestDetector.WRONG_PARENT);
        issues.add(ManifestTypoDetector.ISSUE);
        issues.add(MathDetector.ISSUE);
        issues.add(MergeRootFrameLayoutDetector.ISSUE);
        issues.add(MissingClassDetector.INNERCLASS);
        issues.add(MissingClassDetector.INSTANTIATABLE);
        issues.add(MissingClassDetector.MISSING);
        issues.add(MissingIdDetector.ISSUE);
        issues.add(NamespaceDetector.CUSTOM_VIEW);
        issues.add(NamespaceDetector.RES_AUTO);
        issues.add(NamespaceDetector.TYPO);
        issues.add(NamespaceDetector.UNUSED);
        issues.add(NegativeMarginDetector.ISSUE);
        issues.add(NestedScrollingWidgetDetector.ISSUE);
        issues.add(NfcTechListDetector.ISSUE);
        issues.add(NonInternationalizedSmsDetector.ISSUE);
        issues.add(ObsoleteLayoutParamsDetector.ISSUE);
        issues.add(OnClickDetector.ISSUE);
        issues.add(OverdrawDetector.ISSUE);
        issues.add(OverrideDetector.ISSUE);
        issues.add(OverrideConcreteDetector.ISSUE);
        issues.add(ParcelDetector.ISSUE);
        issues.add(PluralsDetector.EXTRA);
        issues.add(PluralsDetector.MISSING);
        issues.add(PluralsDetector.IMPLIED_QUANTITY);
        issues.add(PreferenceActivityDetector.ISSUE);
        issues.add(PrivateKeyDetector.ISSUE);
        issues.add(PrivateResourceDetector.ISSUE);
        issues.add(ProguardDetector.SPLIT_CONFIG);
        issues.add(ProguardDetector.WRONG_KEEP);
        issues.add(PropertyFileDetector.ESCAPE);
        issues.add(PropertyFileDetector.HTTP);
        issues.add(PxUsageDetector.DP_ISSUE);
        issues.add(PxUsageDetector.IN_MM_ISSUE);
        issues.add(PxUsageDetector.PX_ISSUE);
        issues.add(PxUsageDetector.SMALL_SP_ISSUE);
        issues.add(RegistrationDetector.ISSUE);
        issues.add(RelativeOverlapDetector.ISSUE);
        issues.add(RequiredAttributeDetector.ISSUE);
        issues.add(ResourceCycleDetector.CRASH);
        issues.add(ResourceCycleDetector.CYCLE);
        issues.add(ResourcePrefixDetector.ISSUE);
        issues.add(RtlDetector.COMPAT);
        issues.add(RtlDetector.ENABLED);
        issues.add(RtlDetector.SYMMETRY);
        issues.add(RtlDetector.USE_START);
        issues.add(ScrollViewChildDetector.ISSUE);
        issues.add(SdCardDetector.ISSUE);
        issues.add(SecureRandomDetector.ISSUE);
        issues.add(SecureRandomGeneratorDetector.ISSUE);
        issues.add(SecurityDetector.EXPORTED_PROVIDER);
        issues.add(SecurityDetector.EXPORTED_RECEIVER);
        issues.add(SecurityDetector.EXPORTED_SERVICE);
        issues.add(SecurityDetector.OPEN_PROVIDER);
        issues.add(SecurityDetector.WORLD_READABLE);
        issues.add(SecurityDetector.WORLD_WRITEABLE);
        issues.add(ServiceCastDetector.ISSUE);
        issues.add(SetJavaScriptEnabledDetector.ISSUE);
        issues.add(SharedPrefsDetector.ISSUE);
        issues.add(SignatureOrSystemDetector.ISSUE);
        issues.add(SQLiteDetector.ISSUE);
        issues.add(StateListDetector.ISSUE);
        issues.add(StringFormatDetector.ARG_COUNT);
        issues.add(StringFormatDetector.ARG_TYPES);
        issues.add(StringFormatDetector.INVALID);
        issues.add(StringFormatDetector.POTENTIAL_PLURAL);
        issues.add(SupportAnnotationDetector.CHECK_PERMISSION);
        issues.add(SupportAnnotationDetector.CHECK_RESULT);
        issues.add(SupportAnnotationDetector.COLOR_USAGE);
        issues.add(SupportAnnotationDetector.MISSING_PERMISSION);
        issues.add(SupportAnnotationDetector.RANGE);
        issues.add(SupportAnnotationDetector.RESOURCE_TYPE);
        issues.add(SupportAnnotationDetector.THREAD);
        issues.add(SupportAnnotationDetector.TYPE_DEF);
        issues.add(SystemPermissionsDetector.ISSUE);
        issues.add(TextFieldDetector.ISSUE);
        issues.add(TextViewDetector.ISSUE);
        issues.add(TextViewDetector.SELECTABLE);
        issues.add(TitleDetector.ISSUE);
        issues.add(ToastDetector.ISSUE);
        issues.add(TooManyViewsDetector.TOO_DEEP);
        issues.add(TooManyViewsDetector.TOO_MANY);
        issues.add(TranslationDetector.EXTRA);
        issues.add(TranslationDetector.MISSING);
        issues.add(TypoDetector.ISSUE);
        issues.add(TypographyDetector.DASHES);
        issues.add(TypographyDetector.ELLIPSIS);
        issues.add(TypographyDetector.FRACTIONS);
        issues.add(TypographyDetector.OTHER);
        issues.add(TypographyDetector.QUOTES);
        issues.add(UnusedResourceDetector.ISSUE);
        issues.add(UnusedResourceDetector.ISSUE_IDS);
        issues.add(UseCompoundDrawableDetector.ISSUE);
        issues.add(UselessViewDetector.USELESS_LEAF);
        issues.add(UselessViewDetector.USELESS_PARENT);
        issues.add(Utf8Detector.ISSUE);
        issues.add(ViewConstructorDetector.ISSUE);
        issues.add(ViewHolderDetector.ISSUE);
        issues.add(ViewTagDetector.ISSUE);
        issues.add(ViewTypeDetector.ISSUE);
        issues.add(WakelockDetector.ISSUE);
        issues.add(WebViewDetector.ISSUE);
        issues.add(WrongCallDetector.ISSUE);
        issues.add(WrongCaseDetector.WRONG_CASE);
        issues.add(WrongIdDetector.INVALID);
        issues.add(WrongIdDetector.NOT_SIBLING);
        issues.add(WrongIdDetector.UNKNOWN_ID);
        issues.add(WrongIdDetector.UNKNOWN_ID_LAYOUT);
        issues.add(WrongImportDetector.ISSUE);
        issues.add(WrongLocationDetector.ISSUE);
        sIssues = Collections.unmodifiableList(issues);
    }

    /**
     * Constructs a new {@link BuiltinIssueRegistry}
     */
    public BuiltinIssueRegistry() {
    }

    @NonNull
    @Override
    public List<Issue> getIssues() {
        return sIssues;
    }

    @Override
    protected int getIssueCapacity(@NonNull EnumSet<Scope> scope) {
        if (scope.equals(Scope.ALL)) {
            return getIssues().size();
        } else {
            int initialSize = 12;
            if (scope.contains(Scope.RESOURCE_FILE)) {
                initialSize += 75;
            } else if (scope.contains(Scope.ALL_RESOURCE_FILES)) {
                initialSize += 10;
            }
            if (scope.contains(Scope.JAVA_FILE)) {
                initialSize += 55;
            } else if (scope.contains(Scope.CLASS_FILE)) {
                initialSize += 15;
            } else if (scope.contains(Scope.MANIFEST)) {
                initialSize += 30;
            } else if (scope.contains(Scope.GRADLE_FILE)) {
                initialSize += 5;
            }
            return initialSize;
        }
    }

    /**
     * Reset the registry such that it recomputes its available issues.
     * <p>
     * NOTE: This is only intended for testing purposes.
     */


double take(String login, StringBuilder log) throws IOException;

package com.zerocracy.pm.staff;

import java.io.IOException;

public interface Votes {

    /**
     * Vote.
     * @param login GitHub login of the user
     * @param log Log of the take, if any
     * @return Points to give to him (0..1)
     * @throws IOException If fails
     */

    final class Fake implements Votes {

        private final double value;

        /**
         * Ctor.
         *
         * @param vote Vote
         */
        public Fake(final double vote) {
            this.value = vote;
        }

        @Override
        public double take(final String login, final StringBuilder log) {
            return this.value;
        }
    }
}


public void setFileName(String path) {
        if (saveFile != null && saveFile.getAbsolutePath().equals(path)) {
            return;
        }
        File file = new File(path);
        if (file.exists() && !file.canWrite()) {
            saveFile = null;
        } else {
            saveFile = file;
        }
    }

/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://example.com>
 */
/*
 * The org.opensourcephysics.media.core package defines the Open Source Physics
 * media framework for working with video and other media.
 *
 *
 *
 * For additional information and documentation on Open Source Physics,
 * please see <https://www.compadre.org/osp/>.
 */
package org.opensourcephysics.media.core;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.JTextComponent;
import org.opensourcephysics.controls.ControlsRes;
import org.opensourcephysics.controls.OSPLog;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.display.OSPRuntime;

/**
 * This VideoRecorder records to a scratch file which is then copied as needed.
 *
 * @author Douglas Brown
 * @version 1.0
 */
public abstract class ScratchVideoRecorder implements VideoRecorder {

    protected static JFileChooser chooser;

    protected static JTextComponent chooserField;

    // current file extension for chooser
    protected static String ext;

    protected static boolean ignoreChooser;

    protected static String tempDirectory;

    protected static String tempFilePrefix = "osp_";

    // instance fields
    // type of video being recorded
    protected VideoType videoType;

    // dimension of new video
    protected Dimension dim;

    // most recently added frame
    protected Image frameImage;

    // milliseconds
    protected double frameDuration = 100;

    // number of frames recorded
    protected int frameCount;

    // base name of scratch files
    protected String scratchName;

    // appended to base name for uniqueness
    protected int scratchNumber = 0;

    // active scratch file
    protected File scratchFile;

    // scratch file ready to accept added frames
    protected boolean canRecord;

    // scratch file has added frames
    protected boolean hasContent;

    // scratch file has been saved to saveFile
    protected boolean isSaved;

    // file to which scratch will be copied
    protected File saveFile = null;

    // true to ask to save changes
    protected boolean saveChanges = false;

    protected String tempFileBasePath;

    protected String tempFileType = "png";

    protected ArrayList<File> tempFiles = new ArrayList<File>();

    protected String suggestedFileName;

    protected String chosenExtension;

    static {
        tempDirectory = System.getProperty("java.io.tmpdir");
    }

    /**
     * Constructs a ScratchVideoRecorder for the specified video type.
     *
     * @param vidType the video type
     */
    public ScratchVideoRecorder(VideoType vidType) {
        videoType = vidType;
        ext = videoType.getDefaultExtension();
        SimpleDateFormat formatter = new SimpleDateFormat("ssSSS");
        scratchName = tempFilePrefix + formatter.format(new Date());
        ShutdownHook shutdownHook = new ShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutdownHook);
        try {
            createScratch();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        if (chooser == null) {
            chooser = new JFileChooser(new File(OSPRuntime.chooserDir));
            chooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {

                public void propertyChange(PropertyChangeEvent e) {
                    if (!ignoreChooser) {
                        FileFilter filter = (FileFilter) e.getNewValue();
                        if (filter instanceof VideoFileFilter) {
                            VideoFileFilter vidFilter = (VideoFileFilter) filter;
                            String ext = vidFilter.getDefaultExtension();
                            if (ext != null)
                                setChooserExtension(ext);
                        }
                    }
                }
            });
            // find chooser field
            String temp = "untitled.tmp";
            chooser.setSelectedFile(new File(temp));
            chooserField = getTextComponent(chooser, temp);
        }
    }

    /**
     * Creates a new video (scratch file) and sets fileName to null.
     *
     * @throws IOException
     */
    public void createVideo() throws IOException {
        // create scratch file if none
        if (scratchFile == null) {
            createScratch();
            if (scratchFile == null) {
                OSPLog.severe("No scratch file");
                return;
            }
        }
        // replace scratch file with new one if needed
        if (scratchFile != null && hasContent) {
            // save movie if not saved and saveChanges flag is true
            if (saveChanges && !isSaved) {
                String query = MediaRes.getString("ScratchVideoRecorder.Dialog.SaveVideo.Message");
                int n = JOptionPane.showConfirmDialog(null, query, MediaRes.getString("ScratchVideoRecorder.Dialog.SaveVideo.Title"), JOptionPane.YES_NO_OPTION);
                if (n == JOptionPane.YES_OPTION) {
                    saveVideo();
                }
            }
            createScratch();
            saveFile = null;
            // new video will be size of first image unless dim is set externally
            dim = null;
        }
    }

    /**
     * Creates a new video and sets the destination file name. If fileName
     * is null, the user can select a file with a chooser.
     *
     * @param fileName name of the file to which the video will be written
     * @throws IOException
     */
    public void createVideo(String fileName) throws IOException {
        File file = null;
        if (fileName == null) {
            file = selectFile();
            // Canceled by user
            if (file == null)
                return;
        } else
            file = new File(fileName);
        createVideo();
        saveFile = file;
    }

    /**
     * Sets the size of the video.
     *
     * @param dimension the dimensions of the new video
     */
    public void setSize(Dimension dimension) {
        dim = dimension;
    }

    /**
     * Sets the time duration per frame.
     *
     * @param millis the duration per frame in milliseconds
     */
    public void setFrameDuration(double millis) {
        frameDuration = millis;
    }

    /**
     * Adds a video frame with the specified image.
     *
     * @param image the image to be drawn on the video frame.
     * @throws IOException
     */
    public void addFrame(Image image) throws IOException {
        if (image == null) {
            return;
        }
        frameImage = image;
        if ((scratchFile == null) || (hasContent && isSaved)) {
            createVideo();
        }
        if (scratchFile == null) {
            return;
        }
        if (!canRecord) {
            canRecord = startRecording();
            isSaved = false;
            hasContent = false;
        }
        if (canRecord && append(image)) {
            hasContent = true;
            frameCount++;
        }
    }

    /**
     * Gets the current scratch video.
     *
     * @return the video
     * @throws IOException
     */
    public Video getVideo() throws IOException {
        if (isSaved && saveFile != null) {
            return videoType.getVideo(saveFile.getAbsolutePath());
        }
        saveScratch();
        return videoType.getVideo(scratchFile.getAbsolutePath());
    }

    /**
     * Saves the scratch video to the current file or chooser file.
     *
     * @return the full path to the saved file
     * @throws IOException
     */
    public String saveVideo() throws IOException {
        if (saveFile != null) {
            return saveVideo(saveFile.getAbsolutePath());
        }
        return saveVideoAs();
    }

    /**
     * Saves the current scratch video to the specified file name.
     *
     * @param fileName the file name
     * @return the full path of the saved file
     * @throws IOException
     */
    public String saveVideo(String fileName) throws IOException {
        if (scratchFile == null) {
            return null;
        }
        if (fileName == null) {
            return saveVideoAs();
        }
        setFileName(fileName);
        if (saveFile == null) {
            throw new IOException("could not write to read-only file");
        }
        saveScratch();
        // copy scratch to fileName
        int buffer = 8192;
        byte[] data = new byte[buffer];
        int count = 0;
        int total = 0;
        FileInputStream fin = new FileInputStream(scratchFile);
        InputStream in = new BufferedInputStream(fin);
        FileOutputStream fout = new FileOutputStream(saveFile);
        OutputStream out = new BufferedOutputStream(fout);
        while ((count = in.read(data, 0, buffer)) != -1) {
            out.write(data, 0, count);
            total += count;
        }
        out.flush();
        out.close();
        in.close();
        isSaved = true;
        OSPLog.fine("copied " + total + " bytes from " + scratchFile.getName() + " to " + saveFile.getAbsolutePath());
        scratchFile.delete();
        return saveFile.getAbsolutePath();
    }

    /**
     * Saves the scratch video to a file picked from a chooser.
     *
     * @return the full path of the saved file
     * @throws IOException
     */
    public String saveVideoAs() throws IOException {
        File file = selectFile();
        if (file != null) {
            return saveVideo(file.getAbsolutePath());
        }
        return null;
    }

    /**
     * Gets the file name of the destination video.
     * @return the file name
     */
    public String getFileName() {
        return (saveFile == null) ? null : saveFile.getAbsolutePath();
    }

    /**
     * Sets the file name. May be null.
     * @param path the file name
     */

    /**
     * Suggests a file name. May be null.
     * @param name the file name
     */
    public void suggestFileName(String name) {
        suggestedFileName = name;
    }

    /**
     * Discards the current video and resets the recorder to a ready state.
     */
    public void reset() {
        if (scratchFile != null) {
            try {
                saveScratch();
            } catch (IOException e) {
            }
            scratchFile.delete();
        }
        hasContent = false;
    }

    // ________________________________ static methods _________________________________
    /**
     * Sets the extension used in the chooser.
     */
    private static void setChooserExtension(String extension) {
        if (extension != null) {
            ext = extension;
        }
        if (ext != null && chooser != null && chooser.isVisible()) {
            final String name = (chooserField == null) ? "*." + ext : XML.stripExtension(chooserField.getText()) + "." + ext;
            Runnable runner = new Runnable() {

                public void run() {
                    if (chooserField != null) {
                        chooserField.setText(name);
                    } else {
                        File dir = chooser.getCurrentDirectory();
                        String path = XML.getResolvedPath(name, dir.getAbsolutePath());
                        chooser.setSelectedFile(new File(path));
                    }
                }
            };
            SwingUtilities.invokeLater(runner);
        }
    }

    // ________________________________ private methods _________________________________
    /**
     * Creates the scratch file.
     *
     * @throws IOException
     */
    protected void createScratch() throws IOException {
        if (hasContent || scratchFile == null) {
            String fileName = scratchName;
            if (hasContent) {
                fileName += "-" + scratchNumber++;
            }
            reset();
            fileName += getScratchExtension();
            if (tempDirectory != null)
                fileName = tempDirectory + "/" + fileName;
            scratchFile = new File(fileName);
            hasContent = false;
            canRecord = false;
            OSPLog.finest(scratchFile.getAbsolutePath());
        }
    }

    /**
     * Returns the extension to use for the scratch file.
     * @return the extension
     */
    protected String getScratchExtension() {
        return ".tmp";
    }

    /**
     * Shows a save dialog used to set the output movie file.
     * @return the movie file, or none if canceled by user
     */
    protected File selectFile() {
        ignoreChooser = true;
        File file = null;
        chooser.setDialogTitle(MediaRes.getString("VideoIO.Dialog.SaveVideoAs.Title"));
        chooser.resetChoosableFileFilters();
        VideoFileFilter[] filters = videoType.getFileFilters();
        if (filters != null && filters.length > 0) {
            VideoFileFilter preferred = videoType.getDefaultFileFilter();
            if (preferred == null)
                preferred = filters[0];
            chooser.setAcceptAllFileFilterUsed(false);
            for (int i = 0; i < filters.length; i++) {
                chooser.addChoosableFileFilter(filters[i]);
            }
            chooser.setFileFilter(preferred);
            ext = preferred.getDefaultExtension();
        } else {
            chooser.setAcceptAllFileFilterUsed(true);
        }
        String filename = suggestedFileName;
        if (filename == null)
            filename = MediaRes.getString("VideoIO.FileName.Untitled");
        if (ext != null) {
            filename += "." + ext;
        }
        chooser.setSelectedFile(new File(filename));
        // just in case line above not enough!
        chooserField.setText(filename);
        ignoreChooser = false;
        int result = chooser.showDialog(null, MediaRes.getString("Dialog.Button.Save"));
        if (result == JFileChooser.APPROVE_OPTION) {
            file = chooser.getSelectedFile();
            file = getFileToBeSaved(file);
            chosenExtension = XML.getExtension(file.getName());
            if (file.exists()) {
                if (file.canWrite()) {
                    int selected = JOptionPane.showConfirmDialog(null, " \"" + file.getName() + "\" " + MediaRes.getString("VideoIO.Dialog.FileExists.Message"), MediaRes.getString("VideoIO.Dialog.FileExists.Title"), JOptionPane.YES_NO_CANCEL_OPTION);
                    if (selected != JOptionPane.YES_OPTION) {
                        file = null;
                    }
                } else {
                    JOptionPane.showMessageDialog(null, ControlsRes.getString("Dialog.ReadOnly.Message"), ControlsRes.getString("Dialog.ReadOnly.Title"), JOptionPane.PLAIN_MESSAGE);
                    file = null;
                }
            }
        }
        return file;
    }

    /**
     * Return the file that will be saved if the specified file is selected.
     * This is needed by ImageVideoRecorder since it strips and/or appends digits
     * to the selected file name.
     * This default implementation returns the file itself.
     *
     * @param file the file selected with the chooser
     * @return the file (or first file) to be saved
     */
    protected File getFileToBeSaved(File file) {
        return file;
    }

    /**
     * Called by the garbage collector when this recorder is no longer in use.
     */
    @Override
    protected void finalize() {
        deleteTempFiles();
    }

    /**
     * Deletes the temporary files.
     */
    protected void deleteTempFiles() {
        if (tempFiles == null)
            return;
        synchronized (tempFiles) {
            for (File next : tempFiles) {
                next.delete();
            }
            tempFiles.clear();
        }
    }

    private JTextComponent getTextComponent(Container c, String toMatch) {
        Component[] comps = c.getComponents();
        for (int i = 0; i < comps.length; i++) {
            if ((comps[i] instanceof JTextComponent) && toMatch.equals(((JTextComponent) comps[i]).getText())) {
                return (JTextComponent) comps[i];
            }
            if (comps[i] instanceof Container) {
                JTextComponent tc = getTextComponent((Container) comps[i], toMatch);
                if (tc != null) {
                    return tc;
                }
            }
        }
        return null;
    }

    // ______________________________ abstract methods _________________________________
    /**
     * Saves the current video to the scratch file.
     *
     * @throws IOException
     */
    abstract protected void saveScratch() throws IOException;

    /**
     * Starts the video recording process using current dimension dim.
     *
     * @return true if video recording successfully started
     */
    abstract protected boolean startRecording();

    /**
     * Appends a frame to the current video.
     *
     * @param image the image to append
     * @return true if image successfully appended
     */
    abstract protected boolean append(Image image);

    /**
     * A class to delete all scratch files on shutdown.
     */
    class ShutdownHook extends Thread {

        public void run() {
            if (scratchFile != null) {
                try {
                    saveScratch();
                } catch (Exception ex) {
                }
                scratchFile.delete();
            }
            deleteTempFiles();
        }
    }
}


while ((count = in.read(data, 0, buffer)) != -1) {
            out.write(data, 0, count);
            total += count;
        }

/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://example.com>
 */
/*
 * The org.opensourcephysics.media.core package defines the Open Source Physics
 * media framework for working with video and other media.
 *
 *
 *
 * For additional information and documentation on Open Source Physics,
 * please see <https://www.compadre.org/osp/>.
 */
package org.opensourcephysics.media.core;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.JTextComponent;
import org.opensourcephysics.controls.ControlsRes;
import org.opensourcephysics.controls.OSPLog;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.display.OSPRuntime;

/**
 * This VideoRecorder records to a scratch file which is then copied as needed.
 *
 * @author Douglas Brown
 * @version 1.0
 */
public abstract class ScratchVideoRecorder implements VideoRecorder {

    protected static JFileChooser chooser;

    protected static JTextComponent chooserField;

    // current file extension for chooser
    protected static String ext;

    protected static boolean ignoreChooser;

    protected static String tempDirectory;

    protected static String tempFilePrefix = "osp_";

    // instance fields
    // type of video being recorded
    protected VideoType videoType;

    // dimension of new video
    protected Dimension dim;

    // most recently added frame
    protected Image frameImage;

    // milliseconds
    protected double frameDuration = 100;

    // number of frames recorded
    protected int frameCount;

    // base name of scratch files
    protected String scratchName;

    // appended to base name for uniqueness
    protected int scratchNumber = 0;

    // active scratch file
    protected File scratchFile;

    // scratch file ready to accept added frames
    protected boolean canRecord;

    // scratch file has added frames
    protected boolean hasContent;

    // scratch file has been saved to saveFile
    protected boolean isSaved;

    // file to which scratch will be copied
    protected File saveFile = null;

    // true to ask to save changes
    protected boolean saveChanges = false;

    protected String tempFileBasePath;

    protected String tempFileType = "png";

    protected ArrayList<File> tempFiles = new ArrayList<File>();

    protected String suggestedFileName;

    protected String chosenExtension;

    static {
        tempDirectory = System.getProperty("java.io.tmpdir");
    }

    /**
     * Constructs a ScratchVideoRecorder for the specified video type.
     *
     * @param vidType the video type
     */
    public ScratchVideoRecorder(VideoType vidType) {
        videoType = vidType;
        ext = videoType.getDefaultExtension();
        SimpleDateFormat formatter = new SimpleDateFormat("ssSSS");
        scratchName = tempFilePrefix + formatter.format(new Date());
        ShutdownHook shutdownHook = new ShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutdownHook);
        try {
            createScratch();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        if (chooser == null) {
            chooser = new JFileChooser(new File(OSPRuntime.chooserDir));
            chooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {

                public void propertyChange(PropertyChangeEvent e) {
                    if (!ignoreChooser) {
                        FileFilter filter = (FileFilter) e.getNewValue();
                        if (filter instanceof VideoFileFilter) {
                            VideoFileFilter vidFilter = (VideoFileFilter) filter;
                            String ext = vidFilter.getDefaultExtension();
                            if (ext != null)
                                setChooserExtension(ext);
                        }
                    }
                }
            });
            // find chooser field
            String temp = "untitled.tmp";
            chooser.setSelectedFile(new File(temp));
            chooserField = getTextComponent(chooser, temp);
        }
    }

    /**
     * Creates a new video (scratch file) and sets fileName to null.
     *
     * @throws IOException
     */
    public void createVideo() throws IOException {
        // create scratch file if none
        if (scratchFile == null) {
            createScratch();
            if (scratchFile == null) {
                OSPLog.severe("No scratch file");
                return;
            }
        }
        // replace scratch file with new one if needed
        if (scratchFile != null && hasContent) {
            // save movie if not saved and saveChanges flag is true
            if (saveChanges && !isSaved) {
                String query = MediaRes.getString("ScratchVideoRecorder.Dialog.SaveVideo.Message");
                int n = JOptionPane.showConfirmDialog(null, query, MediaRes.getString("ScratchVideoRecorder.Dialog.SaveVideo.Title"), JOptionPane.YES_NO_OPTION);
                if (n == JOptionPane.YES_OPTION) {
                    saveVideo();
                }
            }
            createScratch();
            saveFile = null;
            // new video will be size of first image unless dim is set externally
            dim = null;
        }
    }

    /**
     * Creates a new video and sets the destination file name. If fileName
     * is null, the user can select a file with a chooser.
     *
     * @param fileName name of the file to which the video will be written
     * @throws IOException
     */
    public void createVideo(String fileName) throws IOException {
        File file = null;
        if (fileName == null) {
            file = selectFile();
            // Canceled by user
            if (file == null)
                return;
        } else
            file = new File(fileName);
        createVideo();
        saveFile = file;
    }

    /**
     * Sets the size of the video.
     *
     * @param dimension the dimensions of the new video
     */
    public void setSize(Dimension dimension) {
        dim = dimension;
    }

    /**
     * Sets the time duration per frame.
     *
     * @param millis the duration per frame in milliseconds
     */
    public void setFrameDuration(double millis) {
        frameDuration = millis;
    }

    /**
     * Adds a video frame with the specified image.
     *
     * @param image the image to be drawn on the video frame.
     * @throws IOException
     */
    public void addFrame(Image image) throws IOException {
        if (image == null) {
            return;
        }
        frameImage = image;
        if ((scratchFile == null) || (hasContent && isSaved)) {
            createVideo();
        }
        if (scratchFile == null) {
            return;
        }
        if (!canRecord) {
            canRecord = startRecording();
            isSaved = false;
            hasContent = false;
        }
        if (canRecord && append(image)) {
            hasContent = true;
            frameCount++;
        }
    }

    /**
     * Gets the current scratch video.
     *
     * @return the video
     * @throws IOException
     */
    public Video getVideo() throws IOException {
        if (isSaved && saveFile != null) {
            return videoType.getVideo(saveFile.getAbsolutePath());
        }
        saveScratch();
        return videoType.getVideo(scratchFile.getAbsolutePath());
    }

    /**
     * Saves the scratch video to the current file or chooser file.
     *
     * @return the full path to the saved file
     * @throws IOException
     */
    public String saveVideo() throws IOException {
        if (saveFile != null) {
            return saveVideo(saveFile.getAbsolutePath());
        }
        return saveVideoAs();
    }

    /**
     * Saves the current scratch video to the specified file name.
     *
     * @param fileName the file name
     * @return the full path of the saved file
     * @throws IOException
     */
    public String saveVideo(String fileName) throws IOException {
        if (scratchFile == null) {
            return null;
        }
        if (fileName == null) {
            return saveVideoAs();
        }
        setFileName(fileName);
        if (saveFile == null) {
            throw new IOException("could not write to read-only file");
        }
        saveScratch();
        // copy scratch to fileName
        int buffer = 8192;
        byte[] data = new byte[buffer];
        int count = 0;
        int total = 0;
        FileInputStream fin = new FileInputStream(scratchFile);
        InputStream in = new BufferedInputStream(fin);
        FileOutputStream fout = new FileOutputStream(saveFile);
        OutputStream out = new BufferedOutputStream(fout);

        out.flush();
        out.close();
        in.close();
        isSaved = true;
        OSPLog.fine("copied " + total + " bytes from " + scratchFile.getName() + " to " + saveFile.getAbsolutePath());
        scratchFile.delete();
        return saveFile.getAbsolutePath();
    }

    /**
     * Saves the scratch video to a file picked from a chooser.
     *
     * @return the full path of the saved file
     * @throws IOException
     */
    public String saveVideoAs() throws IOException {
        File file = selectFile();
        if (file != null) {
            return saveVideo(file.getAbsolutePath());
        }
        return null;
    }

    /**
     * Gets the file name of the destination video.
     * @return the file name
     */
    public String getFileName() {
        return (saveFile == null) ? null : saveFile.getAbsolutePath();
    }

    /**
     * Sets the file name. May be null.
     * @param path the file name
     */
    public void setFileName(String path) {
        if (saveFile != null && saveFile.getAbsolutePath().equals(path)) {
            return;
        }
        File file = new File(path);
        if (file.exists() && !file.canWrite()) {
            saveFile = null;
        } else {
            saveFile = file;
        }
    }

    /**
     * Suggests a file name. May be null.
     * @param name the file name
     */
    public void suggestFileName(String name) {
        suggestedFileName = name;
    }

    /**
     * Discards the current video and resets the recorder to a ready state.
     */
    public void reset() {
        if (scratchFile != null) {
            try {
                saveScratch();
            } catch (IOException e) {
            }
            scratchFile.delete();
        }
        hasContent = false;
    }

    // ________________________________ static methods _________________________________
    /**
     * Sets the extension used in the chooser.
     */
    private static void setChooserExtension(String extension) {
        if (extension != null) {
            ext = extension;
        }
        if (ext != null && chooser != null && chooser.isVisible()) {
            final String name = (chooserField == null) ? "*." + ext : XML.stripExtension(chooserField.getText()) + "." + ext;
            Runnable runner = new Runnable() {

                public void run() {
                    if (chooserField != null) {
                        chooserField.setText(name);
                    } else {
                        File dir = chooser.getCurrentDirectory();
                        String path = XML.getResolvedPath(name, dir.getAbsolutePath());
                        chooser.setSelectedFile(new File(path));
                    }
                }
            };
            SwingUtilities.invokeLater(runner);
        }
    }

    // ________________________________ private methods _________________________________
    /**
     * Creates the scratch file.
     *
     * @throws IOException
     */
    protected void createScratch() throws IOException {
        if (hasContent || scratchFile == null) {
            String fileName = scratchName;
            if (hasContent) {
                fileName += "-" + scratchNumber++;
            }
            reset();
            fileName += getScratchExtension();
            if (tempDirectory != null)
                fileName = tempDirectory + "/" + fileName;
            scratchFile = new File(fileName);
            hasContent = false;
            canRecord = false;
            OSPLog.finest(scratchFile.getAbsolutePath());
        }
    }

    /**
     * Returns the extension to use for the scratch file.
     * @return the extension
     */
    protected String getScratchExtension() {
        return ".tmp";
    }

    /**
     * Shows a save dialog used to set the output movie file.
     * @return the movie file, or none if canceled by user
     */
    protected File selectFile() {
        ignoreChooser = true;
        File file = null;
        chooser.setDialogTitle(MediaRes.getString("VideoIO.Dialog.SaveVideoAs.Title"));
        chooser.resetChoosableFileFilters();
        VideoFileFilter[] filters = videoType.getFileFilters();
        if (filters != null && filters.length > 0) {
            VideoFileFilter preferred = videoType.getDefaultFileFilter();
            if (preferred == null)
                preferred = filters[0];
            chooser.setAcceptAllFileFilterUsed(false);
            for (int i = 0; i < filters.length; i++) {
                chooser.addChoosableFileFilter(filters[i]);
            }
            chooser.setFileFilter(preferred);
            ext = preferred.getDefaultExtension();
        } else {
            chooser.setAcceptAllFileFilterUsed(true);
        }
        String filename = suggestedFileName;
        if (filename == null)
            filename = MediaRes.getString("VideoIO.FileName.Untitled");
        if (ext != null) {
            filename += "." + ext;
        }
        chooser.setSelectedFile(new File(filename));
        // just in case line above not enough!
        chooserField.setText(filename);
        ignoreChooser = false;
        int result = chooser.showDialog(null, MediaRes.getString("Dialog.Button.Save"));
        if (result == JFileChooser.APPROVE_OPTION) {
            file = chooser.getSelectedFile();
            file = getFileToBeSaved(file);
            chosenExtension = XML.getExtension(file.getName());
            if (file.exists()) {
                if (file.canWrite()) {
                    int selected = JOptionPane.showConfirmDialog(null, " \"" + file.getName() + "\" " + MediaRes.getString("VideoIO.Dialog.FileExists.Message"), MediaRes.getString("VideoIO.Dialog.FileExists.Title"), JOptionPane.YES_NO_CANCEL_OPTION);
                    if (selected != JOptionPane.YES_OPTION) {
                        file = null;
                    }
                } else {
                    JOptionPane.showMessageDialog(null, ControlsRes.getString("Dialog.ReadOnly.Message"), ControlsRes.getString("Dialog.ReadOnly.Title"), JOptionPane.PLAIN_MESSAGE);
                    file = null;
                }
            }
        }
        return file;
    }

    /**
     * Return the file that will be saved if the specified file is selected.
     * This is needed by ImageVideoRecorder since it strips and/or appends digits
     * to the selected file name.
     * This default implementation returns the file itself.
     *
     * @param file the file selected with the chooser
     * @return the file (or first file) to be saved
     */
    protected File getFileToBeSaved(File file) {
        return file;
    }

    /**
     * Called by the garbage collector when this recorder is no longer in use.
     */
    @Override
    protected void finalize() {
        deleteTempFiles();
    }

    /**
     * Deletes the temporary files.
     */
    protected void deleteTempFiles() {
        if (tempFiles == null)
            return;
        synchronized (tempFiles) {
            for (File next : tempFiles) {
                next.delete();
            }
            tempFiles.clear();
        }
    }

    private JTextComponent getTextComponent(Container c, String toMatch) {
        Component[] comps = c.getComponents();
        for (int i = 0; i < comps.length; i++) {
            if ((comps[i] instanceof JTextComponent) && toMatch.equals(((JTextComponent) comps[i]).getText())) {
                return (JTextComponent) comps[i];
            }
            if (comps[i] instanceof Container) {
                JTextComponent tc = getTextComponent((Container) comps[i], toMatch);
                if (tc != null) {
                    return tc;
                }
            }
        }
        return null;
    }

    // ______________________________ abstract methods _________________________________
    /**
     * Saves the current video to the scratch file.
     *
     * @throws IOException
     */
    abstract protected void saveScratch() throws IOException;

    /**
     * Starts the video recording process using current dimension dim.
     *
     * @return true if video recording successfully started
     */
    abstract protected boolean startRecording();

    /**
     * Appends a frame to the current video.
     *
     * @param image the image to append
     * @return true if image successfully appended
     */
    abstract protected boolean append(Image image);

    /**
     * A class to delete all scratch files on shutdown.
     */
    class ShutdownHook extends Thread {

        public void run() {
            if (scratchFile != null) {
                try {
                    saveScratch();
                } catch (Exception ex) {
                }
                scratchFile.delete();
            }
            deleteTempFiles();
        }
    }
}