content: /**
   *  Copyright (c) 2013-2016 Angelo ZERRR 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:
   *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
   *  Piotr Tomiak <piotr@genutiec.com> - asynchronous request processing and 
  - *  									refactoring of collectors API 
   */
  package tern.server;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.locks.ReentrantReadWriteLock;
  
  import tern.ITernFileSynchronizer;
  import tern.ITernProject;
  import tern.TernException;
  import tern.server.protocol.ITernResultsCollector;
  import tern.server.protocol.TernDoc;
  import tern.server.protocol.push.IMessageHandler;
  
  /**
   * Abstract tern server.
   * 
   */
  public abstract class AbstractTernServer implements ITernServer {
  
  	private final ITernProject project;
  
  	private final List<ITernServerListener> serverListeners;
  	private final Map<String, List<IMessageHandler>> messageListeners;
  	
  	private boolean dataAsJsonString;
  	private boolean dispose;
  	private boolean loadingLocalPlugins;
  
  	private ITernServerRequestProcessor reqProcessor;
  
  	private ReentrantReadWriteLock stateLock = new ReentrantReadWriteLock();
  
  	public AbstractTernServer(ITernProject project) {
  		this.project = project;
  		this.serverListeners = new ArrayList<ITernServerListener>();
  		this.messageListeners = new HashMap<String, List<IMessageHandler>>();
  		final ITernFileSynchronizer fileSynchronizer = getFileSynchronizer();
  		if (fileSynchronizer != null) {
  			this.addServerListener(new TernServerAdapter() {
  				@Override
  				public void onStop(ITernServer server) {
  					fileSynchronizer.cleanIndexedFiles();
  				}
  			});
  		}
  	}
  
  	protected void beginReadState() {
  		stateLock.readLock().lock();
  	}
  
  	protected void endReadState() {
  		stateLock.readLock().unlock();
  	}
  
  	protected void beginWriteState() {
  		stateLock.writeLock().lock();
  	}
  
  	protected void endWriteState() {
  		stateLock.writeLock().unlock();
  	}
  
  	public boolean isDataAsJsonString() {
  		return dataAsJsonString;
  	}
  
  	public void setDataAsJsonString(boolean dataAsJsonString) {
  		this.dataAsJsonString = dataAsJsonString;
  	}
  
  	@Override
  	public void addServerListener(ITernServerListener listener) {
  		synchronized (serverListeners) {
  			serverListeners.add(listener);
  		}
  	}
  
  	@Override
  	public void removeServerListener(ITernServerListener listener) {
  		synchronized (serverListeners) {
  			serverListeners.remove(listener);
  		}
  	}
  
  	protected void fireStartServer() {
  		synchronized (serverListeners) {
  			for (ITernServerListener listener : serverListeners) {
  				listener.onStart(this);
  			}
  		}
  	}
  
  	protected void fireEndServer() {
  		synchronized (serverListeners) {
  			for (ITernServerListener listener : serverListeners) {
  				listener.onStop(this);
  			}
  		}
  	}
  
  	@Override
  	public final void dispose() {
  		beginWriteState();
  		try {
  			if (!isDisposed()) {
  				this.dispose = true;
  				doDispose();
  				// fireEndServer();
  			}
  		} finally {
  			endWriteState();
  		}
  	}
  
  	@Override
  	public boolean isDisposed() {
  		return dispose;
  	}
  
  	protected abstract void doDispose();
  
  	@Override
  	public ITernFileSynchronizer getFileSynchronizer() {
  		if (project != null) {
  			return project.getFileSynchronizer();
  		}
  		return null;
  	}
  
  	public ITernProject getProject() {
  		return project;
  	}
  
  	@Override
  	public void addFile(String name, String text) {
  		addFile(name, text, null);
  	}
  
  	@Override
  	public void setLoadingLocalPlugins(boolean loadingLocalPlugins) {
  		this.loadingLocalPlugins = loadingLocalPlugins;
  	}
  
  	@Override
  	public boolean isLoadingLocalPlugins() {
  		return loadingLocalPlugins;
  	}
  
  	@Override
  	public void request(TernDoc doc, ITernResultsCollector collector)
  			throws TernException {
  		try {
  			if (reqProcessor == null) {
  				// always provide request processor
  				reqProcessor = new SynchronousRequestProcessor(this);
  			}
  			reqProcessor.processRequest(doc, collector);
  		} catch (Exception e) {
  			getFileSynchronizer().uploadFailed(doc);
  			if (e instanceof TernException) {
  				throw (TernException) e;
  			}
  			throw new TernException(e);
  		}
  	}
  
  	@Override
  	public ITernServerRequestProcessor getRequestProcessor() {
  		return reqProcessor;
  	}
  
  	@Override
  	public void setRequestProcessor(ITernServerRequestProcessor reqProcessor) {
  		this.reqProcessor = reqProcessor;
  	}
  
  	@Override
  	public void on(String type, IMessageHandler handler) {
  		synchronized (messageListeners) {
  			List<IMessageHandler> handlers = messageListeners.get(type);
  			if (handlers == null) {
  				handlers = new ArrayList<IMessageHandler>();
  				messageListeners.put(type, handlers);
  			}
  			if (!handlers.contains(handler)) {
  				handlers.add(handler);
  			}
  		}
  	}
  
  	@Override
  	public void off(String type, IMessageHandler handler) {
  		synchronized (messageListeners) {
  			List<IMessageHandler> handlers = messageListeners.get(type);
  			if (handlers != null) {
  				handlers.remove(handler);
  			}
  		}
  	}
  
  	protected void fireOnMessage(String type, Object jsonObject) {
  		synchronized (messageListeners) {
  			List<IMessageHandler> handlers = messageListeners.get(type);
  			if (handlers != null) {
  				for (IMessageHandler handler : handlers) {
  					handler.handleMessage(jsonObject, getJSONObjectHelper());
  				}
  			}
  		}
  	}
  
  }
  