/* (non-Javadoc)
  * @see Job#setPriority(int)
  */
 protected void setPriority(int newPriority) {
   switch (newPriority) {
     case Job.INTERACTIVE:
     case Job.SHORT:
     case Job.LONG:
     case Job.BUILD:
     case Job.DECORATE:
       manager.setPriority(this, newPriority);
       break;
     default:
       throw new IllegalArgumentException(String.valueOf(newPriority));
   }
 }
 /* (non-Javadoc)
  * @see Job#wakeUp(long)
  */
 protected void wakeUp(long delay) {
   manager.wakeUp(this, delay);
 }
 /* (non-Javadoc)
  * @see Job#yieldRule()
  */
 protected Job yieldRule(IProgressMonitor progressMonitor) {
   return manager.yieldRule(this, progressMonitor);
 }
 /* (non-Javadoc)
  * @see Job#sleep()
  */
 protected boolean sleep() {
   return manager.sleep(this);
 }
 /* (non-Javadoc)
  * @see Job#setRule(ISchedulingRule)
  * @GuardedBy("manager.lock")
  */
 protected void setRule(ISchedulingRule rule) {
   manager.setRule(this, rule);
 }
 /* (non-Javadoc)
  * @see Job#setProgressGroup(IProgressMonitor, int)
  */
 protected void setProgressGroup(IProgressMonitor group, int ticks) {
   Assert.isNotNull(group);
   IProgressMonitor pm = manager.createMonitor(this, group, ticks);
   if (pm != null) setProgressMonitor(pm);
 }
 /* (non-Javadoc)
  * @see Job#schedule(long)
  */
 protected void schedule(long delay) {
   if (shouldSchedule()) manager.schedule(this, delay, false);
 }
 /* (non-Javadoc)
  * @see Job#join()
  */
 protected void join() throws InterruptedException {
   manager.join(this);
 }
 /* (non-Javadoc)
  * @see Job#isBlocking()
  */
 protected boolean isBlocking() {
   return manager.isBlocking(this);
 }
/**
 * Internal implementation class for jobs. Clients must not implement this class directly. All jobs
 * must be subclasses of the API <code>org.eclipse.core.runtime.jobs.Job</code> class.
 */
public abstract class InternalJob extends PlatformObject implements Comparable {
  /**
   * Job state code (value 16) indicating that a job has been removed from the wait queue and is
   * about to start running. From an API point of view, this is the same as RUNNING.
   */
  static final int ABOUT_TO_RUN = 0x10;

  /**
   * Job state code (value 32) indicating that a job has passed scheduling precondition checks and
   * is about to be added to the wait queue. From an API point of view, this is the same as WAITING.
   */
  static final int ABOUT_TO_SCHEDULE = 0x20;
  /**
   * Job state code (value 8) indicating that a job is blocked by another currently running job.
   * From an API point of view, this is the same as WAITING.
   */
  static final int BLOCKED = 0x08;
  /**
   * Job state code (value 64) indicating that a job is yielding. From an API point of view, this is
   * the same as WAITING.
   */
  static final int YIELDING = 0x40;

  // flag mask bits
  private static final int M_STATE = 0xFF;
  private static final int M_SYSTEM = 0x0100;
  private static final int M_USER = 0x0200;

  /*
   * flag on a job indicating that it was about to run, but has been canceled
   */
  private static final int M_ABOUT_TO_RUN_CANCELED = 0x0400;

  /*
   * Flag on a job indicating that it was canceled when running. This flag
   * is used to ensure that #canceling is only ever called once on a job in
   * case of recursive cancelation attempts.
   */
  private static final int M_RUN_CANCELED = 0x0800;

  private static int nextJobNumber = 0;
  protected static final JobManager manager = JobManager.getInstance();

  /**
   * Start time constant indicating a job should be started at a time in the infinite future,
   * causing it to sleep forever.
   */
  static final long T_INFINITE = Long.MAX_VALUE;
  /** Start time constant indicating that the job has no start time. */
  static final long T_NONE = -1;

  private volatile int flags = Job.NONE;
  private final int jobNumber = getNextJobNumber();
  private ListenerList listeners = null;
  private volatile IProgressMonitor monitor;
  private String name;
  /** The job ahead of me in a queue or list. @GuardedBy("manager.lock") */
  private InternalJob next;
  /** The job behind me in a queue or list. @GuardedBy("manager.lock") */
  private InternalJob previous;

  private int priority = Job.LONG;
  /** Arbitrary properties (key,value) pairs, attached to a job instance by a third party. */
  private ObjectMap properties;

  /** Volatile because it is usually set via a Worker thread and is read via a client thread. */
  private volatile IStatus result;
  /** @GuardedBy("manager.lock") */
  private ISchedulingRule schedulingRule;
  /**
   * If the job is waiting, this represents the time the job should start by. If this job is
   * sleeping, this represents the time the job should wake up. If this job is running, this
   * represents the delay automatic rescheduling, or -1 if the job should not be
   * rescheduled. @GuardedBy("manager.lock")
   */
  private long startTime;

  /**
   * Stamp added when a job is added to the wait queue. Used to ensure jobs in the wait queue
   * maintain their insertion order even if they are removed from the wait queue temporarily while
   * blocked @GuardedBy("manager.lock")
   */
  private long waitQueueStamp = T_NONE;

  /*
   * The thread that is currently running this job
   */
  private volatile Thread thread = null;

  /**
   * This lock will be held while performing state changes on this job. It is also used as a
   * notifier used to wake up yielding jobs or waiting ThreadJobs when 1) a conflicting job
   * completes and releases a scheduling rule, or 2) when a this job changes state.
   *
   * <p>See also the lock ordering protocol explanation in JobManager's
   * documentation. @GuardedBy("itself")
   */
  final Object jobStateLock = new Object();

  private static synchronized int getNextJobNumber() {
    return nextJobNumber++;
  }

  protected InternalJob(String name) {
    Assert.isNotNull(name);
    this.name = name;
  }

  /* (non-Javadoc)
   * @see Job#addJobListener(IJobChangeListener)
   */
  protected synchronized void addJobChangeListener(IJobChangeListener listener) {
    if (listeners == null) listeners = new ListenerList(ListenerList.IDENTITY);
    listeners.add(listener);
  }

  /**
   * Adds an entry at the end of the list of which this item is the head. @GuardedBy("manager.lock")
   */
  final void addLast(InternalJob entry) {
    InternalJob last = this;
    // find the end of the queue
    while (last.previous != null) last = last.previous;
    // add the new entry to the end of the queue
    last.previous = entry;
    entry.next = last;
    entry.previous = null;
  }

  /* (non-Javadoc)
   * @see Job#belongsTo(Object)
   */
  protected boolean belongsTo(Object family) {
    return false;
  }

  /* (non-Javadoc)
   * @see Job#cancel()
   */
  protected boolean cancel() {
    return manager.cancel(this);
  }

  /* (non-Javadoc)
   * @see Job#canceling()
   */
  protected void canceling() {
    // default implementation does nothing
  }

  /* (on-Javadoc)
   * @see java.lang.Comparable#compareTo(java.lang.Object)
   */
  public final int compareTo(Object otherJob) {
    return ((InternalJob) otherJob).startTime >= startTime ? 1 : -1;
  }

  /* (non-Javadoc)
   * @see Job#done(IStatus)
   */
  protected void done(IStatus endResult) {
    manager.endJob(this, endResult, true);
  }

  /**
   * Returns the job listeners that are only listening to this job. Returns <code>null</code> if
   * this job has no listeners.
   */
  final ListenerList getListeners() {
    return listeners;
  }

  /* (non-Javadoc)
   * @see Job#getName()
   */
  protected String getName() {
    return name;
  }

  /* (non-Javadoc)
   * @see Job#getPriority()
   */
  protected int getPriority() {
    return priority;
  }

  /** Returns the job's progress monitor, or null if it is not running. */
  final IProgressMonitor getProgressMonitor() {
    return monitor;
  }

  /* (non-Javadoc)
   * @see Job#getProperty
   */
  protected Object getProperty(QualifiedName key) {
    // thread safety: (Concurrency001 - copy on write)
    Map temp = properties;
    if (temp == null) return null;
    return temp.get(key);
  }

  /* (non-Javadoc)
   * @see Job#getResult
   */
  protected IStatus getResult() {
    return result;
  }

  /* (non-Javadoc)
   * @see Job#getRule
   */
  protected ISchedulingRule getRule() {
    return schedulingRule;
  }

  /**
   * Returns the time that this job should be started, awakened, or rescheduled, depending on the
   * current state.
   *
   * @return time in milliseconds
   */
  final long getStartTime() {
    return startTime;
  }

  /* (non-Javadoc)
   * @see Job#getState()
   */
  protected int getState() {
    int state = flags & M_STATE;
    switch (state) {
        // blocked and yielding state is equivalent to waiting state for clients
      case YIELDING:
      case BLOCKED:
        return Job.WAITING;
      case ABOUT_TO_RUN:
        return Job.RUNNING;
      case ABOUT_TO_SCHEDULE:
        return Job.WAITING;
      default:
        return state;
    }
  }

  /* (non-javadoc)
   * @see Job.getThread
   */
  protected Thread getThread() {
    return thread;
  }

  /** Returns the raw job state, including internal states no exposed as API. */
  final int internalGetState() {
    return flags & M_STATE;
  }

  /** Must be called from JobManager#setPriority */
  final void internalSetPriority(int newPriority) {
    this.priority = newPriority;
  }

  /** Must be called from JobManager#setRule */
  final void internalSetRule(ISchedulingRule rule) {
    this.schedulingRule = rule;
  }

  /** Must be called from JobManager#changeState */
  final void internalSetState(int i) {
    flags = (flags & ~M_STATE) | i;
  }

  /** Returns whether this job was canceled when it was about to run */
  final boolean isAboutToRunCanceled() {
    return (flags & M_ABOUT_TO_RUN_CANCELED) != 0;
  }

  /** Returns whether this job was canceled when it was running. */
  final boolean isRunCanceled() {
    return (flags & M_RUN_CANCELED) != 0;
  }

  /* (non-Javadoc)
   * @see Job#isBlocking()
   */
  protected boolean isBlocking() {
    return manager.isBlocking(this);
  }

  /** Returns true if this job conflicts with the given job, and false otherwise. */
  final boolean isConflicting(InternalJob otherJob) {
    ISchedulingRule otherRule = otherJob.getRule();
    if (schedulingRule == null || otherRule == null) return false;
    // if one of the rules is a compound rule, it must be asked the question.
    if (schedulingRule.getClass() == MultiRule.class)
      return schedulingRule.isConflicting(otherRule);
    return otherRule.isConflicting(schedulingRule);
  }

  /* (non-javadoc)
   * @see Job.isSystem()
   */
  protected boolean isSystem() {
    return (flags & M_SYSTEM) != 0;
  }

  /* (non-javadoc)
   * @see Job.isUser()
   */
  protected boolean isUser() {
    return (flags & M_USER) != 0;
  }

  /* (non-Javadoc)
   * @see Job#join()
   */
  protected void join() throws InterruptedException {
    manager.join(this);
  }

  /** Returns the next entry (ahead of this one) in the list, or null if there is no next entry */
  final InternalJob next() {
    return next;
  }

  /**
   * Returns the previous entry (behind this one) in the list, or null if there is no previous entry
   */
  final InternalJob previous() {
    return previous;
  }

  /** Removes this entry from any list it belongs to. Returns the receiver. */
  final InternalJob remove() {
    if (next != null) next.setPrevious(previous);
    if (previous != null) previous.setNext(next);
    next = previous = null;
    return this;
  }

  /* (non-Javadoc)
   * @see Job#removeJobListener(IJobChangeListener)
   */
  protected synchronized void removeJobChangeListener(IJobChangeListener listener) {
    if (listeners != null) {
      listeners.remove(listener);
      if (listeners.isEmpty()) listeners = null;
    }
  }

  /* (non-Javadoc)
   * @see Job#run(IProgressMonitor)
   */
  protected abstract IStatus run(IProgressMonitor progressMonitor);

  /* (non-Javadoc)
   * @see Job#schedule(long)
   */
  protected void schedule(long delay) {
    if (shouldSchedule()) manager.schedule(this, delay, false);
  }

  /** Sets whether this job was canceled when it was about to run */
  final void setAboutToRunCanceled(boolean value) {
    flags = value ? flags | M_ABOUT_TO_RUN_CANCELED : flags & ~M_ABOUT_TO_RUN_CANCELED;
  }

  /** Sets whether this job was canceled when it was running */
  final void setRunCanceled(boolean value) {
    flags = value ? flags | M_RUN_CANCELED : flags & ~M_RUN_CANCELED;
  }

  /* (non-Javadoc)
   * @see Job#setName(String)
   */
  protected void setName(String name) {
    Assert.isNotNull(name);
    this.name = name;
  }

  /**
   * Sets the next entry in this linked list of jobs.
   *
   * @param entry
   */
  final void setNext(InternalJob entry) {
    this.next = entry;
  }

  /**
   * Sets the previous entry in this linked list of jobs.
   *
   * @param entry
   */
  final void setPrevious(InternalJob entry) {
    this.previous = entry;
  }

  /* (non-Javadoc)
   * @see Job#setPriority(int)
   */
  protected void setPriority(int newPriority) {
    switch (newPriority) {
      case Job.INTERACTIVE:
      case Job.SHORT:
      case Job.LONG:
      case Job.BUILD:
      case Job.DECORATE:
        manager.setPriority(this, newPriority);
        break;
      default:
        throw new IllegalArgumentException(String.valueOf(newPriority));
    }
  }

  /* (non-Javadoc)
   * @see Job#setProgressGroup(IProgressMonitor, int)
   */
  protected void setProgressGroup(IProgressMonitor group, int ticks) {
    Assert.isNotNull(group);
    IProgressMonitor pm = manager.createMonitor(this, group, ticks);
    if (pm != null) setProgressMonitor(pm);
  }

  /**
   * Sets the progress monitor to use for the next execution of this job, or for clearing the
   * monitor when a job completes.
   *
   * @param monitor a progress monitor
   */
  final void setProgressMonitor(IProgressMonitor monitor) {
    this.monitor = monitor;
  }

  /* (non-Javadoc)
   * @see Job#setProperty(QualifiedName,Object)
   */
  protected void setProperty(QualifiedName key, Object value) {
    // thread safety: (Concurrency001 - copy on write)
    if (value == null) {
      if (properties == null) return;
      ObjectMap temp = (ObjectMap) properties.clone();
      temp.remove(key);
      if (temp.isEmpty()) properties = null;
      else properties = temp;
    } else {
      ObjectMap temp = properties;
      if (temp == null) temp = new ObjectMap(5);
      else temp = (ObjectMap) properties.clone();
      temp.put(key, value);
      properties = temp;
    }
  }

  /**
   * Sets or clears the result of an execution of this job.
   *
   * @param result a result status, or <code>null</code> @GuardedBy("manager.lock")
   */
  final void setResult(IStatus result) {
    this.result = result;
  }

  /* (non-Javadoc)
   * @see Job#setRule(ISchedulingRule)
   * @GuardedBy("manager.lock")
   */
  protected void setRule(ISchedulingRule rule) {
    manager.setRule(this, rule);
  }

  /**
   * Sets a time to start, wake up, or schedule this job, depending on the current state
   *
   * @param time a time in milliseconds @GuardedBy("manager.lock")
   */
  final void setStartTime(long time) {
    startTime = time;
  }

  /* (non-javadoc)
   * @see Job.setSystem
   */
  protected void setSystem(boolean value) {
    if (getState() != Job.NONE) throw new IllegalStateException();
    flags = value ? flags | M_SYSTEM : flags & ~M_SYSTEM;
  }

  /* (non-javadoc)
   * @see Job.setThread
   */
  protected void setThread(Thread thread) {
    this.thread = thread;
  }

  /* (non-javadoc)
   * @see Job.setUser
   */
  protected void setUser(boolean value) {
    if (getState() != Job.NONE) throw new IllegalStateException();
    flags = value ? flags | M_USER : flags & ~M_USER;
  }

  /* (Non-javadoc)
   * @see Job#shouldSchedule
   */
  protected boolean shouldSchedule() {
    return true;
  }

  /* (non-Javadoc)
   * @see Job#sleep()
   */
  protected boolean sleep() {
    return manager.sleep(this);
  }

  /* (non-Javadoc)
   * @see Job#yieldRule()
   */
  protected Job yieldRule(IProgressMonitor progressMonitor) {
    return manager.yieldRule(this, progressMonitor);
  }

  /* (non-Javadoc)
   * Prints a string-based representation of this job instance.
   * For debugging purposes only.
   */
  public String toString() {
    return getName() + "(" + jobNumber + ")"; // $NON-NLS-1$//$NON-NLS-2$
  }

  /* (non-Javadoc)
   * @see Job#wakeUp(long)
   */
  protected void wakeUp(long delay) {
    manager.wakeUp(this, delay);
  }

  /** @param waitQueueStamp The waitQueueStamp to set. @GuardedBy("manager.lock") */
  void setWaitQueueStamp(long waitQueueStamp) {
    this.waitQueueStamp = waitQueueStamp;
  }

  /** @return Returns the waitQueueStamp. @GuardedBy("manager.lock") */
  long getWaitQueueStamp() {
    return waitQueueStamp;
  }
}
 /* (non-Javadoc)
  * @see Job#done(IStatus)
  */
 protected void done(IStatus endResult) {
   manager.endJob(this, endResult, true);
 }
 /* (non-Javadoc)
  * @see Job#cancel()
  */
 protected boolean cancel() {
   return manager.cancel(this);
 }