content: /**
   *  Copyright (c) 2013-2016 Angelo ZERR and Genuitec LLC.
   *  All rights reserved. This program and the accompanying materials
   *  are made available under the terms of the Eclipse Public License v1.0
   *  which accompanies this distribution, and is available at
   *  http://www.eclipse.org/legal/epl-v10.html
   *
   *  Contributors:
   *  Piotr Tomiak <piotr@genuitec.com> - initial API and implementation
   */
  package tern.eclipse.ide.internal.core.resources;
  
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.core.runtime.jobs.Job;
  
  import tern.TernException;
  import tern.eclipse.ide.core.TernCorePlugin;
  import tern.server.ITernServer;
  import tern.server.ITernServerRequestProcessor;
  import tern.server.TernNoTypeFoundAtPositionException;
  import tern.server.protocol.ITernResultsAsyncCollector;
  import tern.server.protocol.ITernResultsAsyncCollector.TimeoutReason;
  import tern.server.protocol.ITernResultsCollector;
  import tern.server.protocol.TernDoc;
  import tern.server.protocol.TernResultsProcessorsFactory;
  
  public class IDETernServerAsyncReqProcessor extends Job implements
  		ITernServerRequestProcessor {
  
  	private static final long TIMEOUT = 750;
  
  	private ITernResultsAsyncCollector collector;
  	private TernDoc doc;
  	private final ITernServer server;
  	private boolean timedOut;
  
  	public IDETernServerAsyncReqProcessor(ITernServer server) {
  		super("Asynchronous server request job"); //$NON-NLS-1$
  		this.server = server;
  	}
  
  	@Override
  	public void processRequest(TernDoc doc, ITernResultsCollector c)
  			throws TernException {
  		if (!(c instanceof ITernResultsAsyncCollector)) {
  			// need to process request synchronously
  			try {
  				TernResultsProcessorsFactory.makeRequestAndProcess(doc, server,
  						c);
  				return;
  			} catch (TernException ex) {
  				throw ex;
  			} catch (Throwable t) {
  				throw new TernException(t);
  			}
  		}
  		ITernResultsAsyncCollector collector = (ITernResultsAsyncCollector) c;
  		long start = System.currentTimeMillis();
  		while (this.collector != null
  				&& (System.currentTimeMillis() - start) < TIMEOUT / 2) {
  			// completion calculation is still taking place
  			// wait a bit for it to finish
  			try {
  				Thread.sleep(25);
  			} catch (InterruptedException e) {
  				break;
  			}
  		}
  		synchronized (this) {
  			if (this.collector != null) {
  				// Some request is still being processed.
  				collector.timeout(TimeoutReason.PREV_OPERATION_NOT_FINISHED);
  				return;
  			}
  			// can schedule
  			this.collector = collector;
  			this.doc = doc;
  			setName(this.collector.getRequestDisplayName());
  			timedOut = false;
  			schedule();
  		}
  		while (this.collector == collector
  				&& (System.currentTimeMillis() - start) < TIMEOUT) {
  			try {
  				Thread.sleep(25);
  			} catch (InterruptedException e) {
  				break;
  			}
  		}
  		synchronized (this) {
  			if (this.collector == collector) {
  				// will info about time outing only if not finished earlier
  				collector.timeout(TimeoutReason.TIMED_OUT);
  				timedOut = true;
  			}
  		}
  	}
  
  	@Override
  	protected IStatus run(IProgressMonitor monitor) {
  		try {
  			monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
  			TernResultsProcessorsFactory.makeRequestAndProcess(doc, server,
  					collector);
  		} catch(TernNoTypeFoundAtPositionException e) {
  			// ignore error
  			// case when user open hyperlink on javascript element like 'function'. In this case tern throws the error
  			// tern.TernException: TernError: No type found at the given position.
  			// This error must be ignored.
  			// See https://github.com/angelozerr/tern.java/issues/392
  		} catch (Throwable e) {
  				TernCorePlugin.getDefault().getLog()
  						.log(new Status(IStatus.ERROR, TernCorePlugin.PLUGIN_ID, e.getMessage(), e));			
  		}
  		// mark collection as done if not timed out earlier
  		synchronized (this) {
  			if (!timedOut) {
  				collector.done();
  			}
  			collector = null;
  		}
  		return Status.OK_STATUS;
  	}
  
  }
  