content: /**
   *  Copyright (c) 2013-2016 Angelo ZERR.
   *  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
   */
  package tern.eclipse.ide.internal.core;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  
  import tern.EcmaVersion;
  import tern.TernException;
  import tern.eclipse.ide.core.IIDETernProject;
  import tern.eclipse.ide.core.IWorkingCopy;
  import tern.eclipse.ide.core.IWorkingCopyListener;
  import tern.eclipse.ide.core.TernCorePlugin;
  import tern.metadata.TernModuleMetadata;
  import tern.repository.ITernRepository;
  import tern.server.ITernModule;
  import tern.server.TernDef;
  import tern.server.TernPlugin;
  import tern.utils.TernModuleHelper;
  
  /**
   * Working copy implementation.
   */
  public class WorkingCopy implements IWorkingCopy {
  
  	private final IIDETernProject project;
  	private final List<IWorkingCopyListener> listeners;
  	private final List<Object> callers;
  	private List<ITernModule> checkedModules;
  	private List<ITernModule> workingCopyModules;
  	private EcmaVersion ecmaVersion;
  	private List<ITernModule> filteredModules;
  
  	public WorkingCopy(IIDETernProject project) {
  		this.project = project;
  		this.callers = new ArrayList<Object>();
  		this.listeners = new ArrayList<IWorkingCopyListener>();
  	}
  
  	public void initialize() throws TernException {
  		clear();
  		// Get local and global tern modules
  		List<ITernModule> allModules = project.getAllModules();
  		// Group by type
  		workingCopyModules = TernModuleHelper.groupByType(allModules);
  		// Filtered modules
  		filteredModules = new ArrayList<ITernModule>();
  		for (ITernModule module : workingCopyModules) {
  			if (!(isIgnoreModule(module, TernDef.ecmascript.getType())
  					|| isIgnoreModule(module, TernPlugin.es_modules.getType())
  					|| isIgnoreModule(module, TernPlugin.doc_comment.getType()))) {
  				filteredModules.add(module);
  			}
  		}
  		// checked modules
  		List<ITernModule> checkedModules = new WorkingCopyModuleList(this,
  				TernCorePlugin.getTernRepositoryManager().getCheckedModules(project, workingCopyModules));
  		this.setCheckedModules(checkedModules);
  		setEcmaVersion(project.getEcmaVersion());
  	}
  	
  	private boolean isIgnoreModule(ITernModule module, String type) {
  		return type.equals(module.getType());
  	}
  
  	public List<ITernModule> getCheckedModules() {
  		return checkedModules;
  	}
  
  	private void setCheckedModules(List<ITernModule> checkedModules) {
  		this.checkedModules = checkedModules;
  	}
  
  	@Override
  	public void call(Object caller) {
  		if (!callers.contains(caller)) {
  			callers.add(caller);
  		}
  	}
  
  	@Override
  	public boolean isDirty() {
  		return callers.size() == 0;
  	}
  
  	@Override
  	public void commit(Object caller) throws IOException {
  		removeCaller(caller);
  		if (isDirty()) {
  			try {
  				// save tern project with updated tern modules
  				ITernRepository repository = project.getRepository();
  				// clear Plugin + JSON Type Definition
  				project.clearPlugins();
  				project.clearLibs();
  				// Add Plugin + JSON Type Definition
  				Collection<String> requiredDependencies = null;
  				ITernModule dependencyModule = null;
  				List<ITernModule> sortedModules = new ArrayList<ITernModule>();
  				for (ITernModule module : this.getCheckedModules()) {
  					TernModuleMetadata metadata = module.getMetadata();
  					if (metadata != null) {
  						// add required dependencies (ex : if ecma6 is checked,
  						// ecma5 must
  						// be added too).
  						requiredDependencies = metadata.getRequiredDependencies(module.getVersion());
  						for (String dependency : requiredDependencies) {
  							dependencyModule = repository.getModule(dependency);
  							if (dependencyModule != null && !sortedModules.contains(dependencyModule)) {
  								sortedModules.add(dependencyModule);
  							}
  						}
  					}
  					if (module != null && !sortedModules.contains(module)) {
  						sortedModules.add(module);
  					}
  
  				}
  				TernModuleHelper.sort(sortedModules);
  				for (ITernModule m : sortedModules) {
  					TernModuleHelper.update(m, project);
  				}
  				if (ecmaVersion != null) {
  					project.setEcmaVersion(ecmaVersion);
  				}
  				project.save();
  
  			} finally {
  				clear();
  			}
  		}
  	}
  
  	@Override
  	public void clear() {
  		this.callers.clear();
  		this.listeners.clear();
  		if (checkedModules != null) {
  			checkedModules.clear();
  		}
  	}
  
  	private void removeCaller(Object caller) {
  		callers.remove(caller);
  	}
  
  	public boolean hasCheckedTernModule(String moduleName) {
  		for (ITernModule checkedModule : checkedModules) {
  			if (moduleName.equals(checkedModule.getName())) {
  				return true;
  			}
  		}
  		return false;
  	}
  
  	public ITernModule getTernModule(String moduleName) throws TernException {
  		for (ITernModule module : workingCopyModules) {
  			if (moduleName.equals(module.getName())) {
  				return module;
  			}
  		}
  		return null;
  	}
  
  	public void addWorkingCopyListener(IWorkingCopyListener listener) {
  		synchronized (listener) {
  			if (!listeners.contains(listener)) {
  				listeners.add(listener);
  			}
  		}
  	}
  
  	public void removeWorkingCopyListener(IWorkingCopyListener listener) {
  		synchronized (listener) {
  			listeners.remove(listener);
  		}
  	}
  
  	void fireSelectionModules(ITernModule module, boolean selected) {
  		synchronized (listeners) {
  			for (IWorkingCopyListener listener : listeners) {
  				listener.moduleSelectionChanged(module, selected);
  			}
  		}
  	}
  
  	public List<ITernModule> getAllModules() {
  		return workingCopyModules;
  	}
  
  	@Override
  	public List<ITernModule> getFilteredModules() {
  		return filteredModules;
  	}
  
  	@Override
  	public IIDETernProject getProject() {
  		return project;
  	}
  
  	@Override
  	public void setEcmaVersion(EcmaVersion ecmaVersion) {
  		this.ecmaVersion = ecmaVersion;
  	}
  
  	@Override
  	public EcmaVersion getEcmaVersion() {
  		return ecmaVersion;
  	}
  }
  