text:
package it.geosolutions.jaiext.scheduler;

import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.Raster;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.OpImage;
import javax.media.jai.PlanarImage;
import javax.media.jai.TileCache;
import javax.media.jai.TileComputationListener;
import javax.media.jai.TileRequest;
import javax.media.jai.TileScheduler;
import javax.media.jai.util.ImagingException;
import javax.media.jai.util.ImagingListener;

import com.sun.media.jai.util.ImageUtil;

/**
 * A class representing a request for non-prefetch background computation of tiles. The object stores the image, the indices of all tiles being
 * requested, and references to all listeners associated with the request.
 *
 * TileRequest methods are not commented.
 */
class Request implements TileRequest {

    private final TileScheduler scheduler;

    final PlanarImage image;

    final List indices;

    final Set listeners;

    final Hashtable tileStatus;

    /**
     * Constructs a Request.
     *
     * @param scheduler The scheduler processing this request.
     * @param image The image for which tiles are being computed.
     * @param tileIndices The indices of the tiles to be computed.
     * @param tileListeners The listeners to be notified of tile computation, cancellation, or failure.
     *
     * @exception IllegalArgumentException if scheduler, image, or tileIndices is null or if
     *            tileIndices is zero-length.
     */
    Request(TileScheduler scheduler, PlanarImage image, Point[] tileIndices,
            TileComputationListener[] tileListeners) {

        // Save a reference to the scheduler.
        if (scheduler == null) {
            throw new IllegalArgumentException(); // Internal error - no message.
        }
        this.scheduler = scheduler;

        // Save a reference to the image.
        if (image == null) {
            throw new IllegalArgumentException(); // Internal error - no message.
        }
        this.image = image;

        // Ensure there is at least one tile in the request.
        if (tileIndices == null || tileIndices.length == 0) {
            // If this happens it is an internal programming error.
            throw new IllegalArgumentException(); // Internal error - no message.
        }

        // Save the tile indices.
        indices = Arrays.asList(tileIndices);

        // Save references to the listeners, if any.
        if (tileListeners != null) {
            int numListeners = tileListeners.length;
            if (numListeners > 0) {
                listeners = new HashSet(numListeners);
                for (int i = 0; i < numListeners; i++) {
                    listeners.add(tileListeners[i]);
                }
            } else {
                listeners = null;
            }
        } else {
            listeners = null;
        }

        // Initialize status table.
        tileStatus = new Hashtable(tileIndices.length);
    }

    // --- TileRequest implementation ---

    public PlanarImage getImage() {
        return image;
    }

    public Point[] getTileIndices() {
        return (Point[]) indices.toArray(new Point[0]);
    }

    public TileComputationListener[] getTileListeners() {
        return (TileComputationListener[]) listeners.toArray(new TileComputationListener[0]);
    }

    public boolean isStatusAvailable() {
        return true;
    }

    public int getTileStatus(int tileX, int tileY) {
        Point p = new Point(tileX, tileY);

        int status;
        if (tileStatus.containsKey(p)) {
            status = ((Integer) tileStatus.get(p)).intValue();
        } else {
            status = TileRequest.TILE_STATUS_PENDING;
        }

        return status;
    }

    public void cancelTiles(Point[] tileIndices) {
        // Forward the call to the scheduler.
        scheduler.cancelTiles(this, tileIndices);
    }
}

/** A job to put in a job queue. */
interface Job {
    /** Computes the job required. */
    void compute();

    /** Returns true if the job is not done. */
    boolean notDone();

    /** Returns the image for which tiles are being computed. */
    PlanarImage getOwner();

    /**
     * Returns true if and only if the job should block the thread which processes it. In this case the scheduler and the processing
     * thread must communicate using wait() and notify().
     */
    boolean isBlocking();

    /** Returns the first exception encountered or null. */
    Exception getException();
}

/**
 * A Job which computes a single tile at a time for a non-prefetch background job queued by the version of scheduleTiles() which returns
 * a TileRequest. This Job notifies all TileComputationListeners of all TileRequests with which
 * this tile is associated of whether the tile was computed or the computation failed.
 */
final class RequestJob implements Job {

    final JAIExtTileScheduler scheduler; // the TileScheduler

    final PlanarImage owner; // the image this tile belongs to

    final int tileX; // tile's X index

    final int tileY; // tile's Y index

    final Raster[] tiles; // the computed tiles

    final int offset; // offset into arrays

    boolean done = false; // flag indicating completion status

    Exception exception = null; // Any exception that might have occured
                                // during computeTile

    /** Constructor. */
    RequestJob(JAIExtTileScheduler scheduler, PlanarImage owner, int tileX, int tileY,
            Raster[] tiles, int offset) {
        this.scheduler = scheduler;
        this.owner = owner;
        this.tileX = tileX;
        this.tileY = tileY;
        this.tiles = tiles;
        this.offset = offset;
    }

    /**
     * Tile computation. Does the actual call to getTile().
     */
    public void compute() {
        // Get the Request List.
        List reqList;
        synchronized (scheduler.tileRequests) {
            // Initialize the tile ID.
            Object tileID = JAIExtTileScheduler.tileKey(owner, tileX, tileY);

            // Remove the List of Requests from the request Map.
            reqList = (List) scheduler.tileRequests.remove(tileID);

            // Remove the tile Job from the job Map.
            scheduler.tileJobs.remove(tileID);
        }

        // Check whether reqList is valid in case job was cancelled while
        // blocking on the tileRequests Map above.
        // XXX Do not need empty check in next line?
        if (reqList != null && !reqList.isEmpty()) {
            // Update tile status to "processing".
            Point p = new Point(tileX, tileY);
            Integer tileStatus = new Integer(TileRequest.TILE_STATUS_PROCESSING);
            Iterator reqIter = reqList.iterator();
            while (reqIter.hasNext()) {
                Request r = (Request) reqIter.next();
                r.tileStatus.put(p, tileStatus);
            }

            try {
                tiles[offset] = owner.getTile(tileX, tileY);
            } catch (Exception e) {
                exception = e;
            } finally {
                // Extract the Set of all TileComputationListeners.
                int numReq = reqList.size();
                Set listeners = JAIExtTileScheduler.getListeners(reqList);

                // XXX Do not need empty check in next line.
                if (listeners != null && !listeners.isEmpty()) {
                    // Get TileRequests as an array for later use.
                    TileRequest[] requests = (TileRequest[]) reqList.toArray(new TileRequest[0]);

                    // Update tile status as needed.
                    tileStatus = new Integer(exception == null ? TileRequest.TILE_STATUS_COMPUTED
                            : TileRequest.TILE_STATUS_FAILED);
                    for (int i = 0; i < numReq; i++) {
                        ((Request) requests[i]).tileStatus.put(p, tileStatus);
                    }

                    // Create an Iterator over the listeners.
                    Iterator iter = listeners.iterator();

                    // Notify listeners.
                    if (exception == null) {
                        // Tile computation successful.
                        while (iter.hasNext()) {
                            TileComputationListener listener = (TileComputationListener) iter
                                    .next();
                            listener.tileComputed(scheduler, requests, owner, tileX, tileY,
                                    tiles[offset]);
                        }
                    } else {
                        // Tile computation unsuccessful.
                        while (iter.hasNext()) {
                            TileComputationListener listener = (TileComputationListener) iter
                                    .next();
                            listener.tileComputationFailure(scheduler, requests, owner, tileX,
                                    tileY, exception);
                        }
                    }
                }
            }
        }
        // Set the flag indicating job completion.
        done = true;
    }

    /**
     * Returns true if the job is not done; that is, the tile is not computed and no exceptions have occurred.
     */
    public boolean notDone() {
        return !done;
    }

    /** Returns the image for which the tile is being computed. */
    public PlanarImage getOwner() {
        return owner;
    }

    /** Always returns true. */
    public boolean isBlocking() {
        return false;
    }

    /** Returns any encountered exception or null. */
    public Exception getException() {
        return exception;
    }

    /** Returns a string representation of the class object. */
    public String toString() {
        String tString = "null";
        if (tiles[offset] != null) {
            tString = tiles[offset].toString();
        }
        return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ": owner = "
                + owner.toString() + " tileX = " + Integer.toString(tileX) + " tileY = "
                + Integer.toString(tileY) + " tile = " + tString;
    }
}

/**
 * A Job which computes one or more tiles at a time for either a prefetch job or a blocking job.
 */
final class TileJob implements Job {

    final JAIExtTileScheduler scheduler; // the TileScheduler

    final boolean isBlocking; // whether the job is blocking

    final PlanarImage owner; // the image this tile belongs to

    final Point[] tileIndices; // the tile indices

    final Raster[] tiles; // the computed tiles

    final int offset; // offset into arrays

    final int numTiles; // number of elements to use in indices array

    boolean done = false; // flag indicating completion status

    Exception exception = null; // The first exception that might have
                                // occured during computeTile

    /** Constructor. */
    TileJob(JAIExtTileScheduler scheduler, boolean isBlocking, PlanarImage owner,
            Point[] tileIndices, Raster[] tiles, int offset, int numTiles) {
        this.scheduler = scheduler;
        this.isBlocking = isBlocking;
        this.owner = owner;
        this.tileIndices = tileIndices;
        this.tiles = tiles;
        this.offset = offset;
        this.numTiles = numTiles;
    }

    /**
     * Tile computation. Does the actual calls to getTile().
     */
    public void compute() {
        exception = scheduler.compute(owner, tileIndices, tiles, offset, numTiles, null);
        done = true;
    }

    /**
     * Returns true if the job is not done; that is, the tile is not computed and no exceptions have occurred.
     */
    public boolean notDone() {
        return !done;
    }

    /** Returns the image for which tiles are being computed. */
    public PlanarImage getOwner() {
        return owner;
    }

    /** Returns true if and only if there is a listener. */
    public boolean isBlocking() {
        return isBlocking;
    }

    /** Returns any encountered exception or null. */
    public Exception getException() {
        return exception;
    }
}

/**
 * Worker thread that takes jobs from the tile computation queue and does the actual computation.
 */
class WorkerThread extends Thread {

    /** Object indicating the the thread should exit. */
    public static final Object TERMINATE = new Object();

    /** The scheduler that spawned this thread. */
    JAIExtTileScheduler scheduler;

    /** Whether this is a prefetch thread. */
    boolean isPrefetch;

    /** Constructor. */
    public WorkerThread(ThreadGroup group, JAIExtTileScheduler scheduler, boolean isPrefetch) {
        super(group, group.getName() + group.activeCount());
        this.scheduler = scheduler;
        this.isPrefetch = isPrefetch;

        setDaemon(true);
        start();
    }

    /** Does the tile computation. */
    public void run() {
        LinkedList jobQueue = scheduler.getQueue(isPrefetch);

        while (true) {
            Object dequeuedObject = null;

            // Check the job queue.
            synchronized (jobQueue) {
                if (jobQueue.size() > 0) {
                    // Remove the first job.
                    dequeuedObject = jobQueue.removeFirst();
                } else {
                    try {
                        // Wait for a notify() on the queue.
                        jobQueue.wait();
                        continue;
                    } catch (InterruptedException ie) {
                        // Ignore: should never happen.
                    }
                }
            }

            if (dequeuedObject == TERMINATE || getThreadGroup() == null
                    || getThreadGroup().isDestroyed()) {
                // Remove WorkerThread from appropriate Vector.
                Vector threads;
                synchronized (threads = scheduler.getWorkers(isPrefetch)) {
                    threads.remove(this);
                }
                // Exit the thread.
                return;
            }

            Job job = (Job) dequeuedObject;

            // Execute tile job.
            if (job != null) {
                job.compute();

                // Notify the scheduler only if the Job is blocking.
                if (job.isBlocking()) {
                    synchronized (scheduler) {
                        scheduler.notify();
                    }
                }
            }
        } // infinite loop
    }
}

/**
 * This is another implementation of the javax.media.jai.TileScheduler interface. It provides a mechanism for scheduling tile
 * calculation. Multi-threading is used whenever possible.
 *
 * @see javax.media.jai.TileScheduler
 */
public class JAIExtTileScheduler implements TileScheduler {

    /** Logger associated to the scheduler*/
    public final static Logger LOGGER = Logger.getLogger(JAIExtTileScheduler.class.getName());

    /** The default number of worker threads. */
    private static final int NUM_THREADS_DEFAULT = 2;

    /** The default number of worker threads. */
    private static final int NUM_PREFETCH_THREADS_DEFAULT = 1;

    /**
     * The instance counter. It is used to compose the name of the ThreadGroup.
     */
    private static int numInstances = 0;

    /**
     * The tile scheduler name. It is used to compose the name of the ThreadGroup.
     */
    private static String name = JaiI18N.getString("JaiExtTileScheduler_Name");

    /**
     * The root ThreadGroup, which holds two sub-groups: the ThreadGroup for the standard jobs, and the ThreadGroup for the prefetch jobs.
     */
    private ThreadGroup rootGroup;

    /** The ThreadGroup contains all the standard jobs. */
    private ThreadGroup standardGroup;

    /** The ThreadGroup contains all the prefetch jobs. */
    private ThreadGroup prefetchGroup;

    /** The worker thread parallelism. */
    private int parallelism = NUM_THREADS_DEFAULT;

    /** The processing thread parallelism. */
    private int prefetchParallelism = NUM_PREFETCH_THREADS_DEFAULT;

    /** The worker thread priority. */
    private int priority = Thread.NORM_PRIORITY;

    /** The prefetch thread priority. */
    private int prefetchPriority = Thread.MIN_PRIORITY;

    /** A job queue for tiles waiting to be computed by the worker threads. */
    private LinkedList queue = null;

    /** A job queue for tiles waiting to be computed by prefetch workers. */
    private LinkedList prefetchQueue = null;

    /**
     * A Vector of WorkerThreads that persist to do the actual tile computation for normal processing. This variable should
     * never be set to null.
     */
    private Vector workers = new Vector();

    /**
     * A Vector of WorkerThreads that persist to do the actual tile computation for prefetch processing. This variable
     * should never be set to null.
     */
    private Vector prefetchWorkers = new Vector();

    /**
     * The effective number of worker threads; may differ from workers.size() due to latency. This value should equal the size of
     * workers less the number of WorkerThread.TERMINATEs in queue.
     */
    private int numWorkerThreads = 0;

    /**
     * The effective number of prefetch worker threads; may differ from prefetchWorkers.size() due to latency. This value should equal
     * the size of prefetchWorkers less the number of WorkerThread.TERMINATEs in prefetchQueue.
     */
    private int numPrefetchThreads = 0;

    /**
     * Map of tiles currently being computed. The key is created from the image and tile indices by the tileKey() method.
     * Each key is mapped to an Object[1] which may contain null, a Raster, or an indefinite
     * Object which represent, respectively, that the tile is being computed, the tile itself, and that the tile computation failed.
     */
    private Map tilesInProgress = new HashMap();

    /**
     * Map of tiles to Requests. The key is created from the image and tile indices by the tileKey() method.
     * Each key is mapped to a List of Request for the tile. If there is no mapping for the tile, then there are no current
     * requests. If a mapping exists, it should always be non-null and the List value should have size of at least unity.
     */
    Map tileRequests = new HashMap();

    /**
     * Map of tiles to Jobs.The key is created from the image and tile indices by the tileKey() method. Each
     * key is mapped to a Job for the tile. If there is no mapping for the tile, then there is no enqueued RequestJob.
     */
    Map tileJobs = new HashMap();

    /** The name of this instance. */
    private String nameOfThisInstance;

    /**
     * Returns the hash table "key" as a Object for this tile. For PlanarImage and SerializableRenderedImage,
     * the key is generated by the method ImageUtilgenerateID(Object) . For the other cases, a Long object is returned. The
     * upper 32 bits for this Long is the tile owner's hash code, and the lower 32 bits is the tile's index.
     */
    static Object tileKey(PlanarImage owner, int tileX, int tileY) {
        long idx = tileY * (long) owner.getNumXTiles() + tileX;

        BigInteger imageID = (BigInteger) owner.getImageID();
        byte[] buf = imageID.toByteArray();
        int length = buf.length;
        byte[] buf1 = new byte[length + 8];
        System.arraycopy(buf, 0, buf1, 0, length);
        for (int i = 7, j = 0; i >= 0; i--, j += 8)
            buf1[length++] = (byte) (idx >> j);
        return new BigInteger(buf1);
    }

    /**
     * Returns all TileComputationListeners for the supplied List of Requests.
     */
    static Set getListeners(List reqList) {
        // Extract the Set of all TileComputationListeners.
        int numReq = reqList.size();
        HashSet listeners = null;
        for (int j = 0; j < numReq; j++) {
            Request req = (Request) reqList.get(j);
            // XXX Do not need empty check in next line.
            if (req.listeners != null && !req.listeners.isEmpty()) {
                if (listeners == null) {
                    listeners = new HashSet();
                }
                listeners.addAll(req.listeners);
            }
        }

        return listeners;
    }

    /**
     * Converts the supplied Exception's stack trace to a String.
     */
    private static String getStackTraceString(Throwable e) {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        PrintStream printStream = new PrintStream(byteStream);
        e.printStackTrace(printStream);
        printStream.flush();
        String stackTraceString = byteStream.toString();
        printStream.close();
        return stackTraceString;
    }

    /**
     * Constructor.
     *
     * @param parallelism The number of worker threads to do tile computation. If this number is less than 1, no multi-threading is used.
     * @param priority The priority of worker threads.
     * @param prefetchParallelism The number of threads to do prefetching. If this number is less than 1, no multi-threading is used.
     * @param prefetchPriority The priority of prefetch threads.
     */
    public JAIExtTileScheduler(int parallelism, int priority, int prefetchParallelism,
            int prefetchPriority) {
        // Create queues and set parallelism and priority to default values.
        this();

        setParallelism(parallelism);
        setPriority(priority);
        setPrefetchParallelism(prefetchParallelism);
        setPrefetchPriority(prefetchPriority);
    }

    /**
     * Constructor. Processing and prefetch queues are created and all parallelism and priority values are set to default values.
     */
    public JAIExtTileScheduler() {
        queue = new LinkedList();
        prefetchQueue = new LinkedList();

        nameOfThisInstance = name + numInstances;
        rootGroup = new ThreadGroup(nameOfThisInstance);
        rootGroup.setDaemon(true);

        standardGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Standard");
        standardGroup.setDaemon(true);

        prefetchGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Prefetch");
        prefetchGroup.setDaemon(true);

        numInstances++;

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "TileScheduler instantiation");
        }
    }

    /**
     * Tile computation. Does the actual calls to getTile().
     */
    Exception compute(PlanarImage owner, Point[] tileIndices, Raster[] tiles, int offset,
            int numTiles, Request request) {
        Exception exception = null;

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Tile Computation");
        }

        int j = offset;
        if (request == null || request.listeners == null) {
            for (int i = 0; i < numTiles; i++, j++) {
                Point p = tileIndices[j];

                try {
                    if(LOGGER.isLoggable(Level.FINEST)){
                        LOGGER.log(Level.FINEST, "Requested tile: ("+ p.x + ", " + p.y +")");
                    }
                    tiles[j] = owner.getTile(p.x, p.y);
                } catch (Exception e) {
                    exception = e;

                    // Abort the remaining tiles in the job.
                    break;
                }
            }
        } else { // listeners present
            Request[] reqs = new Request[] { request };
            for (int i = 0; i < numTiles; i++, j++) {
                Point p = tileIndices[j];

                // Update tile status to "processing".
                Integer tileStatus = new Integer(TileRequest.TILE_STATUS_PROCESSING);
                request.tileStatus.put(p, tileStatus);

                try {
                    if(LOGGER.isLoggable(Level.FINEST)){
                        LOGGER.log(Level.FINEST, "Requested tile: ("+ p.x + ", " + p.y +")");
                    }
                    tiles[j] = owner.getTile(p.x, p.y);
                    Iterator iter = request.listeners.iterator();
                    while (iter.hasNext()) {
                        // Update tile status to "computed".
                        tileStatus = new Integer(TileRequest.TILE_STATUS_COMPUTED);
                        request.tileStatus.put(p, tileStatus);

                        TileComputationListener listener = (TileComputationListener) iter.next();
                        listener.tileComputed(this, reqs, owner, p.x, p.y, tiles[j]);
                    }
                } catch (Exception e) {
                    exception = e;

                    // Abort the remaining tiles in the job.
                    break;
                }
                /*
                 * XXX try { List reqList; synchronized(tileRequests) { Long tileID = tileKey(owner, p.x, p.y); reqList =
                 * (List)tileRequests.remove(tileID); tileJobs.remove(tileID); } if(reqList != null) { tiles[j] = owner.getTile(p.x, p.y);
                 * TileRequest[] reqs = (TileRequest[])reqList.toArray(new TileRequest[0]); Set listeners = getListeners(reqList); if(listeners !=
                 * null) { Iterator iter = listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener =
                 * (TileComputationListener)iter.next(); listener.tileComputed(this, reqs, owner, p.x, p.y, tiles[j]); } } } } catch (Exception e) {
                 * exception = e;
                 *
                 * // Abort the remaining tiles in the job. break; }
                 */
            }
        }

        // If an exception occured, notify listeners that all remaining
        // tiles in the job have failed.
        if (exception != null && request != null && request.listeners != null) {
            if(LOGGER.isLoggable(Level.FINEST)){
                LOGGER.log(Level.FINEST, "Error on tile computation");
            }
            int lastOffset = j;
            int numFailed = numTiles - (lastOffset - offset);

            // Mark all tiles starting with the one which generated the
            // Exception as "failed".
            for (int i = 0, k = lastOffset; i < numFailed; i++) {
                Integer tileStatus = new Integer(TileRequest.TILE_STATUS_FAILED);
                request.tileStatus.put(tileIndices[k++], tileStatus);
            }

            // Notify listeners.
            Request[] reqs = new Request[] { request };
            for (int i = 0, k = lastOffset; i < numFailed; i++) {
                Point p = tileIndices[k++];
                Iterator iter = request.listeners.iterator();
                while (iter.hasNext()) {
                    TileComputationListener listener = (TileComputationListener) iter.next();
                    listener.tileComputationFailure(this, reqs, owner, p.x, p.y, exception);
                }
            }
        }

        /*
         * XXX if(exception != null) { int numFailed = numTiles - (j - offset); for(int i = 0; i < numFailed; i++) { Point p = tileIndices[j++]; Long
         * tileID = tileKey(owner, p.x, p.y); List reqList = (List)tileRequests.remove(tileID); tileJobs.remove(tileID); if(reqList != null) {
         * TileRequest[] reqs = (TileRequest[])reqList.toArray(new TileRequest[0]); Set listeners = getListeners(reqList); if(listeners != null) {
         * Iterator iter = listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next();
         * listener.tileComputationFailure(this, reqs, owner, p.x, p.y, exception); } } } } }
         */

        return exception;
    }

    /**
     * Schedules a single tile for computation.
     *
     * @param owner The image the tiles belong to.
     * @param tileX The tile's X index.
     * @param tileY The tile's Y index.
     *
     * @exception IllegalArgumentException if owner is null.
     *
     * @return The computed tile
     */
    //
    // This method blocks on the 'tilesInProgress' Map to avoid simultaneous
    // computation of the same tile in two or more different threads. The idea
    // is to release the resources of all but one thread so that the computation
    // occurs more quickly. The synchronization variable is an Object[] of length
    // unity. The computed tile is passed from the computing thread to the
    // waiting threads via the contents of this Object[]. Thus this method does
    // not depend on the TileCache to transfer the data.
    //
    public Raster scheduleTile(OpImage owner, int tileX, int tileY) {
        if (owner == null) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_1"));
        }

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Scheduled tile: ("+ tileX + ", " + tileY +")");
        }

        // Eventual tile to be returned.
        Raster tile = null;

        // Get the tile's unique ID.
        Object tileID = tileKey(owner, tileX, tileY);

        // Set the computation flag and initialize or retrieve the tile cache.
        boolean computeTile = false;
        Object[] cache = null;
        synchronized (tilesInProgress) {
            if (computeTile = !tilesInProgress.containsKey(tileID)) {
                // Computing: add tile ID to the map.
                tilesInProgress.put(tileID, cache = new Object[1]);
            } else {
                // Waiting: get tile cache from the Map.
                cache = (Object[]) tilesInProgress.get(tileID);
            }
        }

        if (computeTile) {
            try {
                try {
                    if(LOGGER.isLoggable(Level.FINEST)){
                        LOGGER.log(Level.FINEST, "Computing tile: ("+ tileX + ", " + tileY +")");
                    }
                    // Attempt to compute the tile.
                    tile = owner.computeTile(tileX, tileY);
                } catch (OutOfMemoryError e) {
                    // Empty the cache and call System.gc()
                    TileCache tileCache = owner.getTileCache();
                    if (tileCache != null) {
                        tileCache.flush();
                        System.gc(); // slow
                    }

                    if(LOGGER.isLoggable(Level.FINEST)){
                        LOGGER.log(Level.FINEST, "OOM Error. Retrying to compute tile: ("+ tileX + ", " + tileY +")");
                    }
                    // Re-attempt to compute the tile.
                    tile = owner.computeTile(tileX, tileY);
                }
            } catch (Throwable e) {
                // Re-throw the Error or Exception.
                if (e instanceof Error) {
                    throw (Error) e;
                } else if (e instanceof RuntimeException) {
                    sendExceptionToListener(JaiI18N.getString("JaiExtTileScheduler_5"), e);
                    // throw (RuntimeException)e;
                } else {
                    String message = JaiI18N.getString("JaiExtTileScheduler_5");
                    sendExceptionToListener(message, new ImagingException(message, e));
                    /*
                     * throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e));
                     */
                }
            } finally {
                synchronized (cache) {
                    // Always set the cached tile to a non-null value.
                    cache[0] = tile != null ? tile : new Object();
                    if(LOGGER.isLoggable(Level.FINEST)){
                        LOGGER.log(Level.FINEST, "Cache notify");
                    }
                    // Notify the thread(s).
                    cache.notifyAll();

                    synchronized (tilesInProgress) {
                        // Remove the tile ID from the Map.
                        tilesInProgress.remove(tileID);
                    }
                }
            }
        } else {
            synchronized (cache) {
                // Check the cache: a null value indicates computation is
                // still in progress.
                if (cache[0] == null) {
                    // Wait for the computation to complete.
                    try {
                        if(LOGGER.isLoggable(Level.FINEST)){
                            LOGGER.log(Level.FINEST, "Cache Waiting");
                        }
                        cache.wait(); // XXX Should there be a timeout?
                    } catch (Exception e) {
                        // XXX What response here?
                    }
                }

                // Set the result only if cache contains a Raster.
                if (cache[0] instanceof Raster) {
                    tile = (Raster) cache[0];
                } else {
                    throw new RuntimeException(JaiI18N.getString("JaiExtTileScheduler_4"));
                }
            }
        }

        return tile;
    }

    /**
     * General purpose method for job creation and queueing. Note that the returned value should be ignored if the listener parameter is
     * non-null.
     *
     * @param owner The image for which tile computation jobs will be queued.
     * @param tileIndices The indices of the tiles to be computed.
     * @param isPrefetch Whether the operation is a prefetch.
     * @param listener A TileComputationListener of the processing. May be null.
     *
     * @return The computed tiles. This value is meaningless if listener is non-null.
     */
    // The allowable arguments are constained as follows:
    // A) owner and tileIndices non-null.
    // B) (isBlocking,isPrefetch) in {(true,false),(false,false),(false,true)}
    // C) listeners != null <=> (isBlocking,isPrefetch) == (false,false)
    // The returned value is one of:
    // Raster[] <=> (isBlocking,isPrefetch) == (true,false)
    // Integer <=> (isBlocking,isPrefetch) == (false,false)
    // (Raster[])null <=> (isBlocking,isPrefetch) == (false,true)
    private Object scheduleJob(PlanarImage owner, Point[] tileIndices, boolean isBlocking,
            boolean isPrefetch, TileComputationListener[] listeners) {
        if (owner == null || tileIndices == null) {
            // null parameters
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_0")); // coding error - no message
        } else if ((isBlocking || isPrefetch) && listeners != null) {
            // listeners for blocking or prefetch job
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_6")); // coding error - no message
        } else if (isBlocking && isPrefetch) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_6")); // coding error - no message
        }

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Scheduling Job");
        }

        int numTiles = tileIndices.length;
        Raster[] tiles = new Raster[numTiles];
        Object returnValue = tiles;

        int numThreads = 0;
        Job[] jobs = null;
        int numJobs = 0;

        synchronized (getWorkers(isPrefetch)) {
            numThreads = getNumThreads(isPrefetch);

            if (numThreads > 0) { // worker threads exist
                if (numTiles <= numThreads || // no more tiles than threads
                        (!isBlocking && !isPrefetch)) { // non-blocking, non-prefetch

                    jobs = new Job[numTiles];

                    if (!isBlocking && !isPrefetch) {
                        Request request = new Request(this, owner, tileIndices, listeners);

                        // Override return value.
                        returnValue = request;

                        // Queue all tiles as single-tile jobs.
                        while (numJobs < numTiles) {
                            Point p = tileIndices[numJobs];

                            Object tileID = tileKey(owner, p.x, p.y);

                            synchronized (tileRequests) {
                                List reqList = null;
                                if (tileRequests.containsKey(tileID)) {
                                    // This tile is already queued in a
                                    // non-blocking, non-prefetch job.
                                    reqList = (List) tileRequests.get(tileID);
                                    reqList.add(request);
                                    numTiles--;
                                } else {
                                    // This tile has not yet been queued.
                                    reqList = new ArrayList();
                                    reqList.add(request);
                                    tileRequests.put(tileID, reqList);

                                    jobs[numJobs] = new RequestJob(this, owner, p.x, p.y, tiles,
                                            numJobs);

                                    tileJobs.put(tileID, jobs[numJobs]);

                                    addJob(jobs[numJobs++], false);
                                }
                            }
                        }
                    } else { // numTiles <= numThreads
                        while (numJobs < numTiles) {
                            jobs[numJobs] = new TileJob(this, isBlocking, owner, tileIndices,
                                    tiles, numJobs, 1);
                            addJob(jobs[numJobs++], isPrefetch);
                        }
                    }
                } else { // more tiles than worker threads
                    // Set the fraction of unqueued tiles to be processed by
                    // each worker thread.
                    float frac = 1.0F / (2.0F * numThreads);

                    // Set the minimum number of tiles each thread may process.
                    // If there is only one thread this will equal the total
                    // number of tiles.
                    int minTilesPerThread = numThreads == 1 ? numTiles : Math.min(
                            Math.max(1, (int) (frac * numTiles / 2.0F + 0.5F)), numTiles);

                    // Allocate the maximum possible number of multi-tile jobs.
                    // This will be larger than the actual number of jobs but
                    // a more precise calcuation is not possible and a dynamic
                    // storage object such as a Collection would not be useful
                    // since as calculated maxNumJobs = 4*numThreads if the
                    // preceeding values of "frac" and "minTilesPerThread" are
                    // 1/(2*numThreads) and frac*numTiles/2, respectively.
                    int maxNumJobs = numThreads == 1 ? 1 : (int) ((float) numTiles
                            / (float) minTilesPerThread + 0.5F);
                    jobs = new TileJob[maxNumJobs];

                    // Set the number of enqueued tiles and the number left.
                    int numTilesQueued = 0;
                    int numTilesLeft = numTiles - numTilesQueued;

                    // Assign a number of tiles to each thread determined by
                    // the number of remaining tiles, the fraction of remaining
                    // tiles to be processed and the minimum chunk size.
                    while (numTilesLeft > 0) {
                        // Set the number of tiles to the pre-calculated
                        // fraction of tiles yet to be computed.
                        int numTilesInThread = (int) (frac * numTilesLeft + 0.5F);

                        // Ensure that the number to be processed is at
                        // least the minimum chunk size.
                        if (numTilesInThread < minTilesPerThread) {
                            numTilesInThread = minTilesPerThread;
                        }

                        // Clamp number of tiles in thread to number unqueued.
                        if (numTilesInThread > numTilesLeft) {
                            numTilesInThread = numTilesLeft;
                        }

                        // Decrement the count of remaining tiles. Note that
                        // this value will be non-negative due to the clamping
                        // above.
                        numTilesLeft -= numTilesInThread;

                        // If the number left is smaller than the minimum chunk
                        // size then process these tiles in the current job.
                        if (numTilesLeft < minTilesPerThread) {
                            numTilesInThread += numTilesLeft;
                            numTilesLeft = 0;
                        }

                        // Create a job to process the number of tiles needed.
                        jobs[numJobs] = new TileJob(this, isBlocking, owner, tileIndices, tiles,
                                numTilesQueued, numTilesInThread);

                        // Queue the job and increment the job count.
                        addJob(jobs[numJobs++], isPrefetch);

                        // Increment the count of tiles queued.
                        numTilesQueued += numTilesInThread;
                    }
                } // SingleTile vs. MultiTile Jobs
            } // numThreads > 0
        } // end synchronized block

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Job Scheduled");
        }

        if (numThreads != 0) {
            // If blocking, wait until all tiles have been computed.
            // There is no 'else' block for non-blocking as in that
            // case we just want to continue.
            if (isBlocking) {
                LinkedList jobQueue = getQueue(isPrefetch);
                for (int i = 0; i < numJobs; i++) {
                    synchronized (this) {
                        while (jobs[i].notDone()) {
                            try {
                                if(LOGGER.isLoggable(Level.FINEST)){
                                    LOGGER.log(Level.FINEST, "Waiting the Job computation");
                                }
                                wait(5000l);
                            } catch (InterruptedException ie) {
                                // Ignore: should never happen.
                            }
                        }
                    }

                    // XXX: should we re-throw the exception or
                    // should we reschedule this job ??
                    Exception e = jobs[i].getException();

                    if (e != null) {
                        // Throw a RuntimeException with the Exception's
                        // message concatenated with the stack trace.
                        String message = JaiI18N.getString("JaiExtTileScheduler_5");
                        sendExceptionToListener(message, new ImagingException(message, e));
                        /*
                         * throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e));
                         */
                    }
                }
            }
        } else { // numThreads == 0
            Request request = null;
            if (!isBlocking && !isPrefetch) {
                request = new Request(this, owner, tileIndices, listeners);
                returnValue = request;
            }

            // no workers; sequentially compute tiles in main thread
            Exception e = compute(owner, tileIndices, tiles, 0, numTiles, request);

            // Throw a RuntimeException with the Exception's
            // message concatenated with the stack trace.
            if (e != null) {
                String message = JaiI18N.getString("JaiExtTileScheduler_5");
                sendExceptionToListener(message, new ImagingException(message, e));
                /*
                 * throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e));
                 */
            }
        }

        return returnValue;
    }

    /**
     * Schedules multiple tiles of an image for computation.
     *
     * @param owner The image the tiles belong to.
     * @param tileIndices An array of tile X and Y indices.
     *
     * @return An array of computed tiles.
     */
    public Raster[] scheduleTiles(OpImage owner, Point tileIndices[]) {
        if (owner == null || tileIndices == null) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_0"));
        }
        return (Raster[]) scheduleJob(owner, tileIndices, true, false, null);
    }

    /**
     * Schedule a list of tiles for computation. The supplied listeners will be notified after each tile has been computed. This method ideally should
     * be non-blocking. If the TileScheduler implementation uses multithreading, it is at the discretion of the implementation which
     * thread invokes the TileComputationListener methods.
     */
    public TileRequest scheduleTiles(PlanarImage target, Point[] tileIndices,
            TileComputationListener[] tileListeners) {
        if (target == null || tileIndices == null) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_0"));
        }
        return (TileRequest) scheduleJob(target, tileIndices, false, false, tileListeners);
    }

    /**
     * Issues an advisory cancellation request to the TileScheduler stating that the indicated tiles of the specified image should not be
     * processed. The handling of this request is at the discretion of the scheduler which may cancel tile processing in progress and remove tiles
     * from its internal queue, remove tiles from the queue but not terminate current processing, or simply do nothing.
     *
     *

     * In this implementation of TileScheduler the second tile cancellation option is implemented, i.e., tiles are removed from the
     * internal queue but computation already in progress is not terminated. If there is at least one worker thread this method should be
     * non-blocking. Any tiles allowed to complete computation subsequent to this call are complete and will be treated as if they had not been
     * cancelled, e.g., with respect to caching, notification of registered listeners, etc. Furthermore, canceling a tile request in no way
     * invalidates the tile as a candidate for future recomputation.
     */
    public void cancelTiles(TileRequest request, Point[] tileIndices) {
        if (request == null) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_3"));
        }

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Removing Tiles");
        }

        Request req = (Request) request;
        synchronized (tileRequests) {
            // Save the list of all tile indices in this request.
            List reqIndexList = req.indices;

            // Initialize the set of tile indices to cancel.
            Point[] indices;
            if (tileIndices != null && tileIndices.length > 0) {
                // Create a Set from the supplied indices.
                List tileIndexList = Arrays.asList(tileIndices);

                // Retain only indices which were actually in the request.
                tileIndexList.retainAll(reqIndexList);

                indices = (Point[]) tileIndexList.toArray(new Point[0]);
            } else {
                indices = (Point[]) reqIndexList.toArray(new Point[0]);
            }

            // Cache the count.
            int numTiles = indices.length;

            // Cache status value.
            Integer tileStatus = new Integer(TileRequest.TILE_STATUS_CANCELLED);

            // Loop over tile indices to be cancelled.
            for (int i = 0; i < numTiles; i++) {
                Point p = indices[i];

                // Get the tile's ID.
                Object tileID = tileKey(req.image, p.x, p.y);

                // Get the list of requests for this tile.
                List reqList = (List) tileRequests.get(tileID);

                // If there are none, proceed to next index.
                if (reqList == null) {
                    continue;
                }

                // Remove this Request from the Request List for this tile.
                reqList.remove(req);

                // If the request list is now empty, dequeue the job and
                // remove the tile from the hashes.
                if (reqList.isEmpty()) {
                    synchronized (queue) {
                        Object job = tileJobs.remove(tileID);
                        if (job != null) {
                            queue.remove(job);
                        }
                    }
                    tileRequests.remove(tileID);
                }

                // Update tile status to "cancelled".
                req.tileStatus.put(p, tileStatus);

                // Notify any listeners.
                if (req.listeners != null) {
                    TileRequest[] reqArray = new TileRequest[] { req };
                    Iterator iter = req.listeners.iterator();
                    while (iter.hasNext()) {
                        TileComputationListener listener = (TileComputationListener) iter.next();
                        listener.tileCancelled(this, reqArray, req.image, p.x, p.y);
                    }
                }

                if(LOGGER.isLoggable(Level.FINEST)){
                    LOGGER.log(Level.FINEST, "Removed Tile: (" + p.x + ", " + p.y + ")");
                }

            }
        }
    }

    /**
     * Prefetchs a list of tiles of an image.
     *
     * @param owner The image the tiles belong to.
     * @param tileIndices An array of tile X and Y indices.
     */
    public void prefetchTiles(PlanarImage owner, Point[] tileIndices) {
        if (owner == null || tileIndices == null) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_0"));
        }
        scheduleJob(owner, tileIndices, false, true, null);
    }

    /**
     * Suggests to the scheduler the degree of parallelism to use in processing invocations of scheduleTiles(). For example, this might
     * set the number of threads to spawn. It is legal to implement this method as a no-op.
     *
     *

     * In this implementation of TileScheduler this method sets the number of worker threads actually used for tile computation. Ideally this number
     * should equal the number of processors actually available on the system. It is the responsibility of the application to set this value as the
     * number of processors is not available via the virtual machine. A parallelism value of zero indicates that all tile computation will be effected
     * in the primary thread. A parallelism value of N indicates that there will be N worker threads in addition to the primary
     * scheduler thread. In JAI the parallelism defaults to a value of 2 unless explicity set by the application.
     *
     * @param parallelism The suggested degree of parallelism.
     * @throws IllegalArgumentException if parallelism is negative.
     */
    public void setParallelism(int parallelism) {
        if (parallelism < 0) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_2"));
        }
        this.parallelism = parallelism;
    }

    /**
     * Returns the degree of parallelism of the scheduler.
     */
    public int getParallelism() {
        return parallelism;
    }

    /**
     * Identical to setParallelism() but applies only to prefetchTiles().
     */
    public void setPrefetchParallelism(int parallelism) {
        if (parallelism < 0) {
            throw new IllegalArgumentException(JaiI18N.getString("JaiExtTileScheduler_2"));
        }
        prefetchParallelism = parallelism;
    }

    /**
     * Identical to getParallelism() but applies only to prefetchTiles().
     */
    public int getPrefetchParallelism() {
        return prefetchParallelism;
    }

    /**
     * Suggests to the scheduler the priority to assign to processing effected by scheduleTiles(). For example, this might set thread
     * priority. Values outside of the accepted priority range will be clamped to the nearest extremum. An implementation may clamp the prefetch
     * priority to less than the scheduling priority. It is legal to implement this method as a no-op.
     *
     *

     * In this implementation of TileScheduler this method sets the priority of the worker threads used for tile computation. Its initial value is
     * Thread.NORM_PRIORITY.
     *
     * @param priority The suggested priority.
     */
    public void setPriority(int priority) {
        this.priority = Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
    }

    /**
     * Returns the priority of scheduleTiles() processing.
     */
    public int getPriority() {
        return priority;
    }

    /**
     * Identical to setPriority() but applies only to prefetchTiles().
     *
     *

     * In this implementation of TileScheduler, this method sets the priority of any threads spawned to prefetch tiles. Its initial value
     * is Thread.MIN_PRIORITY.
     */
    public void setPrefetchPriority(int priority) {
        prefetchPriority = Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
    }

    /**
     * Identical to getPriority() but applies only to prefetchTiles().
     */
    public int getPrefetchPriority() {
        return prefetchPriority;
    }

    /**
     * Recreate the ThreadGroupis and WorkThreads. This happens in the case of applet: the java plugin will exist after the
     * termination of the applet so that JAI and TileScheduler will also exist. However, the ThreadGroups are destroyed. Thus, the old
     * workers should be terminated and new i ThreadGroup and workers should be created.
     */
    // private synchronized void createThreadGroup(boolean isPrefetch) {
    private void createThreadGroup(boolean isPrefetch) {
        if (rootGroup == null || rootGroup.isDestroyed()) {
            rootGroup = new ThreadGroup(nameOfThisInstance);
            rootGroup.setDaemon(true);
        }

        if (isPrefetch && (prefetchGroup == null || prefetchGroup.isDestroyed())) {
            prefetchGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Prefetch");
            prefetchGroup.setDaemon(true);
        }

        if (!isPrefetch && (standardGroup == null || standardGroup.isDestroyed())) {
            standardGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Standard");
            standardGroup.setDaemon(true);
        }

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Thread Group created");
        }

        Vector thr = getWorkers(isPrefetch);
        int size = thr.size();

        for (int i = size - 1; i >= 0; i--) {
            Thread t = (Thread) thr.get(i);
            if (!t.isAlive())
                thr.remove(t);
        }

        if (isPrefetch)
            numPrefetchThreads = thr.size();
        else
            numWorkerThreads = thr.size();

    }

    /**
     * Returns the effective number of threads of the specified type. This method also updates the number and priority of threads of the specified
     * type according to the global settings. This method may add WorkerThread.TERMINATEs to the appropriate queue if there are too many
     * effective threads.
     */
    private int getNumThreads(boolean isPrefetch) {
        createThreadGroup(isPrefetch);

        // Local variables.
        Vector thr = getWorkers(isPrefetch);
        int nthr;
        int prll;
        int prty;

        // Set local variables depending on the thread type.
        if (isPrefetch) {
            nthr = numPrefetchThreads;
            prll = prefetchParallelism;
            prty = prefetchPriority;
        } else {
            nthr = numWorkerThreads;
            prll = parallelism;
            prty = priority;
        }

        // Update priority if it has changed.
        if (nthr > 0 && ((Thread) thr.get(0)).getPriority() != prty) {
            int size = thr.size();
            for (int i = 0; i < size; i++) {
                Thread t = (Thread) thr.get(i);
                if (t != null && t.getThreadGroup() != null) {
                    t.setPriority(prty);
                }
            }
        }

        if (nthr < prll) {
            // Not enough processing threads.
            // Add more threads at current priority.
            while (nthr < prll) {
                Thread t = new WorkerThread(isPrefetch ? prefetchGroup : standardGroup, this,
                        isPrefetch);

                t.setPriority(prty);
                thr.add(t);
                nthr++;
            }
        } else {
            // Too many processing threads: queue WorkerThread.TERMINATEs.
            // WorkerThread will remove itself later from the appropriate
            // Vector.
            while (nthr > prll) {
                addJob(WorkerThread.TERMINATE, isPrefetch);
                nthr--;
            }
        }

        // Update the number of effective threads.
        if (isPrefetch) {
            numPrefetchThreads = nthr;
        } else {
            numWorkerThreads = nthr;
        }

        return nthr;
    }

    /** Returns the appropriate worker list. */
    Vector getWorkers(boolean isPrefetch) {
        return isPrefetch ? workers : prefetchWorkers;
    }

    /** Returns the appropriate queue. */
    LinkedList getQueue(boolean isPrefetch) {
        return isPrefetch ? prefetchQueue : queue;
    }

    /** Append a job to the appropriate queue. */
    private void addJob(Object job, boolean isPrefetch) {
        if (job == null || (job != WorkerThread.TERMINATE && !(job instanceof Job))) {
            // Programming error: deliberately no message.
            throw new IllegalArgumentException();
        }

        LinkedList jobQueue;
        synchronized (jobQueue = getQueue(isPrefetch)) {
            if (isPrefetch || jobQueue.isEmpty() || job instanceof RequestJob) {
                // Append job to queue.
                jobQueue.addLast(job);
            } else {
                // If the queue is non-empty or the job is a TileJob
                // insert the job after the last TileJob in the queue.
                boolean inserted = false;
                for (int idx = jobQueue.size() - 1; idx >= 0; idx--) {
                    if (jobQueue.get(idx) instanceof TileJob) {
                        jobQueue.add(idx + 1, job);
                        inserted = true;
                        break;
                    }
                }
                if (!inserted) {
                    jobQueue.addFirst(job);
                }
            }
            jobQueue.notify();
        }

        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Added Job to the Queue");
        }
    }

    /** Queue WorkerThread.TERMINATEs to all workers. */
    protected void finalize() throws Throwable {
        terminateAll(false);
        terminateAll(true);
        super.finalize();
    }

    /** Queue WorkerThread.TERMINATEs to all appropriate workers. */
    private void terminateAll(boolean isPrefetch) {
        synchronized (getWorkers(isPrefetch)) {
            int numThreads = isPrefetch ? numPrefetchThreads : numWorkerThreads;
            for (int i = 0; i < numThreads; i++) {
                addJob(WorkerThread.TERMINATE, isPrefetch);
                if (isPrefetch) {
                    numPrefetchThreads--;
                } else {
                    numWorkerThreads--;
                }
            }
        }
        if(LOGGER.isLoggable(Level.FINEST)){
            LOGGER.log(Level.FINEST, "Termination of the " + (isPrefetch? "Prefetch":"Worker") + " Threads");
        }
    }

    void sendExceptionToListener(String message, Throwable e) {
        ImagingListener listener = ImageUtil.getImagingListener((RenderingHints) null);
        listener.errorOccurred(message, e, this, false);
    }
}


text:
class TrimeshFace;

class Trimesh : public MaterialSceneObject
{
	friend class TrimeshFace;
	typedef std::vector<glm::dvec3> Normals;
	typedef std::vector<glm::dvec3> Vertices;
	typedef std::vector<TrimeshFace*> Faces;
	typedef std::vector<Material*> Materials;

	Vertices vertices;
	Faces faces;
	Normals normals;
	Materials materials;
	BoundingBox localBounds;

public:
	Trimesh( Scene *scene, Material *mat, TransformNode *transform )
		: MaterialSceneObject(scene, mat),
		displayListWithMaterials(0),
		displayListWithoutMaterials(0)
	{
		this->transform = transform;
		vertNorms = false;
	}

	bool vertNorms;

	bool intersectLocal(ray& r, isect& i) const;

	~Trimesh();

	// must add vertices, normals, and materials IN ORDER
	void addVertex( const glm::dvec3 & );
	void addMaterial( Material *m );
	void addNormal( const glm::dvec3 & );
	bool addFace( int a, int b, int c );

	const char *doubleCheck();

	void generateNormals();

	bool hasBoundingBoxCapability() const { return true; }

	BoundingBox ComputeLocalBoundingBox()
	{
		BoundingBox localbounds;
		if (vertices.size() == 0) return localbounds;
		localbounds.setMax(vertices[0]);
		localbounds.setMin(vertices[0]);
		Vertices::const_iterator viter;
		for (viter = vertices.begin(); viter != vertices.end(); ++viter)
		{
			localbounds.setMax(glm::max( localbounds.getMax(), *viter));
			localbounds.setMin(glm::min( localbounds.getMin(), *viter));
		}
		localBounds = localbounds;
		return localbounds;
	}

protected:
	void glDrawLocal(int quality, bool actualMaterials, bool actualTextures) const;
	mutable int displayListWithMaterials;
	mutable int displayListWithoutMaterials;
};

class TrimeshFace : public MaterialSceneObject
{
    Trimesh *parent;
    int ids[3];
    glm::dvec3 normal;
    double dist;

public:
    TrimeshFace( Scene *scene, Material *mat, Trimesh *parent, int a, int b, int c)
        : MaterialSceneObject( scene, mat )
    {
        this->parent = parent;
        ids[0] = a;
        ids[1] = b;
        ids[2] = c;

		// Compute the face normal here, not on the fly
		glm::dvec3 a_coords = parent->vertices[a];
		glm::dvec3 b_coords = parent->vertices[b];
		glm::dvec3 c_coords = parent->vertices[c];

		glm::dvec3 vab = (b_coords - a_coords);
		glm::dvec3 vac = (c_coords - a_coords);
		glm::dvec3 vcb = (b_coords - c_coords);

		if (glm::length(vab) == 0.0 || glm::length(vac) == 0.0 || glm::length(vcb) == 0.0)
			degen = true;
		else {
			degen = false;
			normal = glm::cross(b_coords - a_coords, c_coords - a_coords);
			normal = glm::normalize(normal);
			dist = glm::dot(normal, a_coords);
		}
		localbounds = ComputeLocalBoundingBox();
		bounds = localbounds;
    }

	BoundingBox localbounds;
	bool degen;

    int operator[]( int i ) const
    {
        return ids[i];
    }

	glm::dvec3 getNormal()
	{
		return normal;
	}

    bool intersect(ray& r, isect& i ) const;
    bool intersectLocal(ray& r, isect& i ) const;

    bool hasBoundingBoxCapability() const { return true; }

    BoundingBox ComputeLocalBoundingBox()
    {
        BoundingBox localbounds;
        localbounds.setMax(glm::max(parent->vertices[ids[0]], parent->vertices[ids[1]]));
		localbounds.setMin(glm::min( parent->vertices[ids[0]], parent->vertices[ids[1]]));

        localbounds.setMax(glm::max(parent->vertices[ids[2]], localbounds.getMax()));
		localbounds.setMin(glm::min( parent->vertices[ids[2]], localbounds.getMin()));
        return localbounds;
    }

    const BoundingBox& getBoundingBox() const { return localbounds; }

 };

#endif // TRIMESH_H__

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

			<title></title>
			<link rel="stylesheet" href="media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>

Joobsbox_Helpers

Description

			Class trees

			Index of elements

							Todo List

Classes

Joobsbox_Helpers_Event

Joobsbox_Helpers_Filter

Joobsbox_Helpers_TranslationHash

Zend_View_Helper_Event

Zend_View_Helper_Filter

Zend_View_Helper_MakeLink

Zend_View_Helper_TranslateUrl

Files

Event.php

Filter.php

MakeLink.php

TranslateUrl.php

TranslationHash.php

phpDocumentor v 1.4.2


text:
#include <limits.h>
#include <X11/extensions/XvMC.h>

#include "avcodec.h"
#include "dsputil.h"
#include "mpegvideo.h"

#undef NDEBUG
#include <assert.h>

#include "xvmc.h"
#include "xvmc_internal.h"

/**
 * Initialize the block field of the MpegEncContext pointer passed as
 * parameter after making sure that the data is not corrupted.
 * In order to implement something like direct rendering instead of decoding
 * coefficients in s->blocks and then copying them, copy them directly
 * into the data_blocks array provided by xvmc.
 */
void ff_xvmc_init_block(MpegEncContext *s)
{
    struct xvmc_pix_fmt *render = (struct xvmc_pix_fmt*)s->current_picture.data[2];
    assert(render && render->xvmc_id == AV_XVMC_ID);

    s->block = (DCTELEM (*)[64])(render->data_blocks + render->next_free_data_block_num * 64);
}

/**
 * Fill individual block pointers, so there are no gaps in the data_block array
 * in case not all blocks in the macroblock are coded.
 */
void ff_xvmc_pack_pblocks(MpegEncContext *s, int cbp)
{
    int i, j = 0;
    const int mb_block_count = 4 + (1 << s->chroma_format);

    cbp <<= 12-mb_block_count;
    for (i = 0; i < mb_block_count; i++) {
        if (cbp & (1 << 11))
            s->pblocks[i] = &s->block[j++];
        else
            s->pblocks[i] = NULL;
        cbp += cbp;
    }
}

/**
 * Find and store the surfaces that are used as reference frames.
 * This function should be called for every new field and/or frame.
 * It should be safe to call the function a few times for the same field.
 */
int ff_xvmc_field_start(MpegEncContext *s, AVCodecContext *avctx)
{
    struct xvmc_pix_fmt *last, *next, *render = (struct xvmc_pix_fmt*)s->current_picture.data[2];
    const int mb_block_count = 4 + (1 << s->chroma_format);

    assert(avctx);
    if (!render || render->xvmc_id != AV_XVMC_ID ||
        !render->data_blocks || !render->mv_blocks ||
        (unsigned int)render->allocated_mv_blocks   > INT_MAX/(64*6) ||
        (unsigned int)render->allocated_data_blocks > INT_MAX/64     ||
        !render->p_surface) {
        av_log(avctx, AV_LOG_ERROR,
               "Render token doesn't look as expected.\n");
        return -1; // make sure that this is a render packet
    }

    if (render->filled_mv_blocks_num) {
        av_log(avctx, AV_LOG_ERROR,
               "Rendering surface contains %i unprocessed blocks.\n",
               render->filled_mv_blocks_num);
        return -1;
    }
    if (render->allocated_mv_blocks   < 1 ||
        render->allocated_data_blocks <  render->allocated_mv_blocks*mb_block_count ||
        render->start_mv_blocks_num   >= render->allocated_mv_blocks                ||
        render->next_free_data_block_num >
                        render->allocated_data_blocks -
                        mb_block_count*(render->allocated_mv_blocks-render->start_mv_blocks_num)) {
        av_log(avctx, AV_LOG_ERROR,
               "Rendering surface doesn't provide enough block structures to work with.\n");
        return -1;
    }

    render->picture_structure = s->picture_structure;
    render->flags             = s->first_field ? 0 : XVMC_SECOND_FIELD;
    render->p_future_surface  = NULL;
    render->p_past_surface    = NULL;

    switch(s->pict_type) {
        case  FF_I_TYPE:
            return 0; // no prediction from other frames
        case  FF_B_TYPE:
            next = (struct xvmc_pix_fmt*)s->next_picture.data[2];
            if (!next)
                return -1;
            if (next->xvmc_id != AV_XVMC_ID)
                return -1;
            render->p_future_surface = next->p_surface;
            // no return here, going to set forward prediction
        case  FF_P_TYPE:
            last = (struct xvmc_pix_fmt*)s->last_picture.data[2];
            if (!last)
                last = render; // predict second field from the first
            if (last->xvmc_id != AV_XVMC_ID)
                return -1;
            render->p_past_surface = last->p_surface;
            return 0;
    }

return -1;
}

/**
 * Complete frame/field rendering by passing any remaining blocks.
 * Normally ff_draw_horiz_band() is called for each slice, however,
 * some leftover blocks, for example from error_resilience(), may remain.
 * It should be safe to call the function a few times for the same field.
 */
void ff_xvmc_field_end(MpegEncContext *s)
{
    struct xvmc_pix_fmt *render = (struct xvmc_pix_fmt*)s->current_picture.data[2];
    assert(render);

    if (render->filled_mv_blocks_num > 0)
        ff_draw_horiz_band(s, 0, 0);
}

/**
 * Synthesize the data needed by XvMC to render one macroblock of data.
 * Fill all relevant fields, if necessary do IDCT.
 */
void ff_xvmc_decode_mb(MpegEncContext *s)
{
    XvMCMacroBlock *mv_block;
    struct xvmc_pix_fmt *render;
    int i, cbp, blocks_per_mb;

    const int mb_xy = s->mb_y * s->mb_stride + s->mb_x;

    if (s->encoding) {
        av_log(s->avctx, AV_LOG_ERROR, "XVMC doesn't support encoding!!!\n");
        return;
    }

    // from MPV_decode_mb(), update DC predictors for P macroblocks
    if (!s->mb_intra) {
        s->last_dc[0] =
        s->last_dc[1] =
        s->last_dc[2] =  128 << s->intra_dc_precision;
    }

    // MC doesn't skip blocks
    s->mb_skipped = 0;

    // Do I need to export quant when I could not perform postprocessing?
    // Anyway, it doesn't hurt.
    s->current_picture.qscale_table[mb_xy] = s->qscale;

    // start of XVMC-specific code
    render = (struct xvmc_pix_fmt*)s->current_picture.data[2];
    assert(render);
    assert(render->xvmc_id == AV_XVMC_ID);
    assert(render->mv_blocks);

    // take the next free macroblock
    mv_block = &render->mv_blocks[render->start_mv_blocks_num +
                                  render->filled_mv_blocks_num];

    mv_block->x        = s->mb_x;
    mv_block->y        = s->mb_y;
    mv_block->dct_type = s->interlaced_dct; // XVMC_DCT_TYPE_FRAME/FIELD;
    if (s->mb_intra) {
        mv_block->macroblock_type = XVMC_MB_TYPE_INTRA; // no MC, all done
    } else {
        mv_block->macroblock_type = XVMC_MB_TYPE_PATTERN;

        if (s->mv_dir & MV_DIR_FORWARD) {
            mv_block->macroblock_type |= XVMC_MB_TYPE_MOTION_FORWARD;
            // PMV[n][dir][xy] = mv[dir][n][xy]
            mv_block->PMV[0][0][0] = s->mv[0][0][0];
            mv_block->PMV[0][0][1] = s->mv[0][0][1];
            mv_block->PMV[1][0][0] = s->mv[0][1][0];
            mv_block->PMV[1][0][1] = s->mv[0][1][1];
        }
        if (s->mv_dir & MV_DIR_BACKWARD) {
            mv_block->macroblock_type |= XVMC_MB_TYPE_MOTION_BACKWARD;
            mv_block->PMV[0][1][0] = s->mv[1][0][0];
            mv_block->PMV[0][1][1] = s->mv[1][0][1];
            mv_block->PMV[1][1][0] = s->mv[1][1][0];
            mv_block->PMV[1][1][1] = s->mv[1][1][1];
        }

        switch(s->mv_type) {
            case  MV_TYPE_16X16:
                mv_block->motion_type = XVMC_PREDICTION_FRAME;
                break;
            case  MV_TYPE_16X8:
                mv_block->motion_type = XVMC_PREDICTION_16x8;
                break;
            case  MV_TYPE_FIELD:
                mv_block->motion_type = XVMC_PREDICTION_FIELD;
                if (s->picture_structure == PICT_FRAME) {
                    mv_block->PMV[0][0][1] <<= 1;
                    mv_block->PMV[1][0][1] <<= 1;
                    mv_block->PMV[0][1][1] <<= 1;
                    mv_block->PMV[1][1][1] <<= 1;
                }
                break;
            case  MV_TYPE_DMV:
                mv_block->motion_type = XVMC_PREDICTION_DUAL_PRIME;
                if (s->picture_structure == PICT_FRAME) {

                    mv_block->PMV[0][0][0] = s->mv[0][0][0];      // top from top
                    mv_block->PMV[0][0][1] = s->mv[0][0][1] << 1;

                    mv_block->PMV[0][1][0] = s->mv[0][0][0];      // bottom from bottom
                    mv_block->PMV[0][1][1] = s->mv[0][0][1] << 1;

                    mv_block->PMV[1][0][0] = s->mv[0][2][0];      // dmv00, top from bottom
                    mv_block->PMV[1][0][1] = s->mv[0][2][1] << 1; // dmv01

                    mv_block->PMV[1][1][0] = s->mv[0][3][0];      // dmv10, bottom from top
                    mv_block->PMV[1][1][1] = s->mv[0][3][1] << 1; // dmv11

                } else {
                    mv_block->PMV[0][1][0] = s->mv[0][2][0];      // dmv00
                    mv_block->PMV[0][1][1] = s->mv[0][2][1];      // dmv01
                }
                break;
            default:
                assert(0);
        }

        mv_block->motion_vertical_field_select = 0;

        // set correct field references
        if (s->mv_type == MV_TYPE_FIELD || s->mv_type == MV_TYPE_16X8) {
            mv_block->motion_vertical_field_select |= s->field_select[0][0];
            mv_block->motion_vertical_field_select |= s->field_select[1][0] << 1;
            mv_block->motion_vertical_field_select |= s->field_select[0][1] << 2;
            mv_block->motion_vertical_field_select |= s->field_select[1][1] << 3;
        }
    } // !intra
    // time to handle data blocks
    mv_block->index = render->next_free_data_block_num;

    blocks_per_mb = 6;
    if (s->chroma_format >= 2) {
        blocks_per_mb = 4 + (1 << s->chroma_format);
    }

    // calculate cbp
    cbp = 0;
    for (i = 0; i < blocks_per_mb; i++) {
        cbp += cbp;
        if (s->block_last_index[i] >= 0)
            cbp++;
    }

    if (s->flags & CODEC_FLAG_GRAY) {
        if (s->mb_intra) {                                   // intra frames are always full chroma blocks
            for (i = 4; i < blocks_per_mb; i++) {
                memset(s->pblocks[i], 0, sizeof(*s->pblocks[i]));  // so we need to clear them
                if (!render->unsigned_intra)
                    *s->pblocks[i][0] = 1 << 10;
            }
        } else {
            cbp &= 0xf << (blocks_per_mb - 4);
            blocks_per_mb = 4;                               // luminance blocks only
        }
    }
    mv_block->coded_block_pattern = cbp;
    if (cbp == 0)
        mv_block->macroblock_type &= ~XVMC_MB_TYPE_PATTERN;

    for (i = 0; i < blocks_per_mb; i++) {
        if (s->block_last_index[i] >= 0) {
            // I do not have unsigned_intra MOCO to test, hope it is OK.
            if (s->mb_intra && (render->idct || (!render->idct && !render->unsigned_intra)))
                *s->pblocks[i][0] -= 1 << 10;
            if (!render->idct) {
                s->dsp.idct(*s->pblocks[i]);
                /* It is unclear if MC hardware requires pixel diff values to be
                 * in the range [-255;255]. TODO: Clipping if such hardware is
                 * ever found. As of now it would only be an unnecessary
                 * slowdown. */
            }
            // copy blocks only if the codec doesn't support pblocks reordering
            if (s->avctx->xvmc_acceleration == 1) {
                memcpy(&render->data_blocks[render->next_free_data_block_num*64],
                       s->pblocks[i], sizeof(*s->pblocks[i]));
            }
            render->next_free_data_block_num++;
        }
    }
    render->filled_mv_blocks_num++;

    assert(render->filled_mv_blocks_num     <= render->allocated_mv_blocks);
    assert(render->next_free_data_block_num <= render->allocated_data_blocks);
    /* The above conditions should not be able to fail as long as this function
     * is used and the following 'if ()' automatically calls a callback to free
     * blocks. */

    if (render->filled_mv_blocks_num == render->allocated_mv_blocks)
        ff_draw_horiz_band(s, 0, 0);
}


text:
<% if (contest.state === 1) { %>
    <% var startsAt = (contest.startsAt instanceof Date) ? contest.startsAt : new Date(contest.startsAt) %>
    <span class="navbar-text text-primary" title="Contest starts on <%- moment(startsAt).utc().format('MMM D [at] HH:mm [UTC]') %> (<%- moment(startsAt).fromNow() %>)">
         Not started

<% } else if (contest.state === 2) { %>
    <% var finishesAt = (contest.finishesAt instanceof Date) ? contest.finishesAt : new Date(contest.finishesAt) %>
    <span class="navbar-text text-success" title="Contest finishes on <%- moment(finishesAt).utc().format('MMM D [at] HH:mm [UTC]') %> (<%- moment(finishesAt).fromNow() %>)">
         Started

<% } else if (contest.state === 3) { %>

         Paused

<% } else if (contest.state === 4) { %>
    <% var finishesAt = (contest.finishesAt instanceof Date) ? contest.finishesAt : new Date(contest.finishesAt) %>
    <span class="navbar-text text-danger" title="Contest has finished on <%- moment(finishesAt).utc().format('MMM D [at] HH:mm [UTC]') %> (<%- moment(finishesAt).fromNow() %>)">
         Finished

<% } %>


text:
import * as Lint from 'tslint';
import * as ts from 'typescript';
import { NgWalker } from './angular/ngWalker';
import { ComponentMetadata } from './angular/metadata';
export declare class Rule extends Lint.Rules.AbstractRule {
    static metadata: Lint.IRuleMetadata;
    static SELECTOR_FAILURE: string;
    apply(sourceFile: ts.SourceFile): Lint.RuleFailure[];
}
export declare class EnforceComponentSelectorValidatorWalker extends NgWalker {
    protected visitNgComponent(metadata: ComponentMetadata): void;
}


text:
<?php

namespace backend\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\models\UserPack;

/**
 * UserSearch represents the model behind the search form about `common\models\User`.
 */
class UserPackSearch extends UserPack
{
    public $lytis;

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'valiuta', 'role', 'status', 'profileVisits', 'activated', 'updated_at', 'lastOnline', 'new', 'newDone', 'adminChat', 'facebook', 'photoLimit', 'firstMsg'], 'integer'],
            [['username', 'auth_key', 'password_hash', 'password_reset_token', 'email', 'reg_step', 'avatar', 'fb_id', 'msgEmailNot'], 'safe'],
            [['expires', 'vip', 'created_at', 'gimimoTS', 'iesko', 'orentacija', 'f'], 'safe']
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    public function VipSD()
    {
        $left = (isset($_GET['l']))? $_GET['l']: 3;

        $query = UserPack::find()->where(['vip' => 1])->andWhere(['<=', 'expires', time() + $left * 24 * 60 * 60]);

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => (isset($_GET['ps']))? $_GET['ps'] : 30,
            ],
            'sort'=> ['defaultOrder' => ['expires'=>SORT_ASC]],
        ]);

        return $dataProvider;
;    }

    public function getUsers($params)
    {
        var_dump($params);
    }

}


text:
/**
 *
 */
package com.zhigu.controllers.goods;

import java.io.UnsupportedEncodingException;
import java.util.List;

import net.sf.json.JSONArray;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.zhigu.model.Result;
import com.zhigu.service.goods.IGoodsService;
import com.zhigu.service.user.ICartService;

/**
 * @author liukailong 商品的ajax请求控制器
 */
@Controller
@RequestMapping("/ajax/goods")
public class AjaxGoodsController {

	/** 注入商品业务处理接口 */
	@Autowired
	private IGoodsService goodsService;

	@Autowired
	private ICartService cartService;

	/**
	 *
	 * 商品名称自动补全控制器
	 *
	 * @param keyword
	 * @return json对象
	 * @throws UnsupportedEncodingException
	 *
	 */
	@RequestMapping("/names")
	@ResponseBody
	public JSONArray getGoodsNames(String term) throws UnsupportedEncodingException {

		JSONArray json = new JSONArray();

		List<String> goods = goodsService.queryGoodsNames(term);

		if (goods != null) {

			json = JSONArray.fromObject(goods);

		}

		return json;

	}

	/**
	 * sku 组合查询
	 *
	 * @param goodsId
	 * @param sku1
	 * @param sku2
	 * @return
	 */
	@RequestMapping("/sku")
	@ResponseBody
	public Result getGoodsSkuByPropIdStr(int goodsId, String skuStr) {

		if (StringUtils.isBlank(skuStr)) {

			return new Result(false, "未正确选择销售属性");

		}
		return new Result(true, goodsService.queryGoodsSkuByPropStr(goodsId, skuStr));

	}

}


text:
using System;

public class Program
{
    public static void Main(string[] args)
    {
        var limit = new DateTime(2199, 1, 1, 0, 0, 0);
        limit.AddYears(1);
        Console.WriteLine(limit);
    }
}


text:
#ifndef HTMLObjectElement_h
#define HTMLObjectElement_h

#include "core/CoreExport.h"
#include "core/html/FormAssociated.h"
#include "core/html/HTMLPlugInElement.h"
#include "core/html/ListedElement.h"

namespace blink {

class HTMLFormElement;

// Inheritance of ListedElement was used for NPAPI form association, but
// is still kept here so that legacy APIs such as form attribute can keep
// working according to the spec.  See:
// https://html.spec.whatwg.org/multipage/embedded-content.html#the-object-element
class CORE_EXPORT HTMLObjectElement final : public HTMLPlugInElement,
                                            public ListedElement,
                                            public FormAssociated {
  DEFINE_WRAPPERTYPEINFO();
  USING_GARBAGE_COLLECTED_MIXIN(HTMLObjectElement);

 public:
  static HTMLObjectElement* create(Document&,
                                   bool createdByParser);
  ~HTMLObjectElement() override;
  DECLARE_VIRTUAL_TRACE();

  const String& classId() const { return m_classId; }

  HTMLFormElement* formOwner() const override;

  bool containsJavaApplet() const;

  bool hasFallbackContent() const override;
  bool useFallbackContent() const override;
  bool canRenderFallbackContent() const override { return true; }
  void renderFallbackContent() override;

  bool isFormControlElement() const override { return false; }

  bool isEnumeratable() const override { return true; }
  bool isInteractiveContent() const override;

  // Implementations of constraint validation API.
  // Note that the object elements are always barred from constraint validation.
  String validationMessage() const override { return String(); }
  bool checkValidity() { return true; }
  bool reportValidity() { return true; }
  void setCustomValidity(const String&) override {}

  bool canContainRangeEndPoint() const override { return useFallbackContent(); }

  bool isExposed() const;

  bool willUseFallbackContentAtLayout() const;

  FormAssociated* toFormAssociatedOrNull() override { return this; };
  void associateWith(HTMLFormElement*) override;

 private:
  HTMLObjectElement(Document&, bool createdByParser);

  void parseAttribute(const AttributeModificationParams&) override;
  bool isPresentationAttribute(const QualifiedName&) const override;
  void collectStyleForPresentationAttribute(const QualifiedName&,
                                            const AtomicString&,
                                            MutableStylePropertySet*) override;

  InsertionNotificationRequest insertedInto(ContainerNode*) override;
  void removedFrom(ContainerNode*) override;

  void didMoveToNewDocument(Document& oldDocument) override;

  void childrenChanged(const ChildrenChange&) override;

  bool isURLAttribute(const Attribute&) const override;
  bool hasLegalLinkAttribute(const QualifiedName&) const override;
  const QualifiedName& subResourceAttributeName() const override;
  const AtomicString imageSourceURL() const override;

  LayoutPart* existingLayoutPart() const override;

  void updateWidgetInternal() override;
  void updateDocNamedItem();

  void reattachFallbackContent();

  // FIXME: This function should not deal with url or serviceType
  // so that we can better share code between <object> and <embed>.
  void parametersForPlugin(Vector<String>& paramNames,
                           Vector<String>& paramValues,
                           String& url,
                           String& serviceType);

  bool hasValidClassId() const;

  void reloadPluginOnAttributeChange(const QualifiedName&);

  bool shouldRegisterAsNamedItem() const override { return true; }
  bool shouldRegisterAsExtraNamedItem() const override { return true; }

  String m_classId;
  bool m_useFallbackContent : 1;
};

// Intentionally left unimplemented, template specialization needs to be
// provided for specific return types.
template <typename T>
inline const T& toElement(const ListedElement&);
template <typename T>
inline const T* toElement(const ListedElement*);

// Make toHTMLObjectElement() accept a ListedElement as input instead of
// a Node.
template <>
inline const HTMLObjectElement* toElement<HTMLObjectElement>(
    const ListedElement* element) {
  SECURITY_DCHECK(!element || !element->isFormControlElement());
  const HTMLObjectElement* objectElement =
      static_cast<const HTMLObjectElement*>(element);
  // We need to assert after the cast because ListedElement doesn't
  // have hasTagName.
  SECURITY_DCHECK(!objectElement ||
                  objectElement->hasTagName(HTMLNames::objectTag));
  return objectElement;
}

template <>
inline const HTMLObjectElement& toElement<HTMLObjectElement>(
    const ListedElement& element) {
  SECURITY_DCHECK(!element.isFormControlElement());
  const HTMLObjectElement& objectElement =
      static_cast<const HTMLObjectElement&>(element);
  // We need to assert after the cast because ListedElement doesn't
  // have hasTagName.
  SECURITY_DCHECK(objectElement.hasTagName(HTMLNames::objectTag));
  return objectElement;
}

}  // namespace blink

#endif  // HTMLObjectElement_h


text:
__author__ = 'Liudmila'
import random
from model.group import Group
from model.contact import Contact

def test_add_contact_from_homepage_in_group(app, db, check_ui):
    old_groups = db.get_group_list()
    old_contacts = db.get_contact_list()
    old_contacts_in_group = db.get_contact_in_group()
    group = random.choice(old_groups)
    contact = random.choice(old_contacts)
    contacts_in_ui_group = app.contact.get_contact_list_in_group(group.name)
    if contact not in contacts_in_ui_group:
        app.contact.select_contact_by_id(contact.id)
        app.contact.replace_in_group(group.name)
    new_contacts_in_group = db.get_contact_in_group()
    old_contacts_in_group.append(contact.id, group.id)
    assert sorted(old_contacts_in_group, key=Group.id_or_max) == sorted(new_contacts_in_group, key=Group.id_or_max)

    if check_ui:
        contacts_in_ui_group = app.contact.get_contact_list_in_group(group.name)
        contacts_in_db_group = []
        for item in new_contacts_in_group:
            if item in contacts_in_ui_group:
                contacts_in_db_group.append(item)
        assert sorted(contacts_in_ui_group, key=Group.id_or_max) == sorted(contacts_in_db_group, key=Group.id_or_max)