/*******************************************************************************
* Copyright (c) 2011 Gerd Wuetherich (gerd@gerd-wuetherich.de).
* 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:
* Gerd Wuetherich (gerd@gerd-wuetherich.de) - initial API and implementation
******************************************************************************/
package org.bundlemaker.core.internal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.bundlemaker.core.IBundleMakerProject;
import org.bundlemaker.core.common.Activator;
import org.bundlemaker.core.common.Constants;
import org.bundlemaker.core.internal.modules.modularizedsystem.ModularizedSystem;
import org.bundlemaker.core.internal.parser.ModelSetup;
import org.bundlemaker.core.internal.parser.XYZService;
import org.bundlemaker.core.internal.transformation.BasicProjectContentTransformation;
import org.bundlemaker.core.parser.IProblem;
import org.bundlemaker.core.project.BundleMakerProjectContentChangedEvent;
import org.bundlemaker.core.project.BundleMakerProjectDescriptionChangedEvent;
import org.bundlemaker.core.project.BundleMakerProjectState;
import org.bundlemaker.core.project.BundleMakerProjectStateChangedEvent;
import org.bundlemaker.core.project.IBundleMakerProjectChangedListener;
import org.bundlemaker.core.project.IModifiableProjectDescription;
import org.bundlemaker.core.project.IProjectDescription;
import org.bundlemaker.core.project.IProjectDescriptionAwareBundleMakerProject;
import org.bundlemaker.core.project.internal.BundleMakerProjectCache;
import org.bundlemaker.core.project.internal.BundleMakerProjectDescription;
import org.bundlemaker.core.project.internal.ProjectDescriptionStore;
import org.bundlemaker.core.resource.IBundleMakerProjectHook;
import org.bundlemaker.core.resource.IModularizedSystem;
import org.bundlemaker.core.resource.ITransformation;
import org.bundlemaker.core.spi.store.IPersistentDependencyStore;
import org.bundlemaker.core.spi.store.IPersistentDependencyStoreFactory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.osgi.util.tracker.ServiceTracker;
/**
* <p>
* Implementation of the interface {@link IProjectDescriptionAwareBundleMakerProject}.
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public class BundleMakerProject implements IBundleMakerProject {
/** the associated eclipse project (the bundle make project) */
private IProject _project;
/** the user attributes */
private Map<String, Object> _userAttributes;
/** the bundle maker project description */
private BundleMakerProjectDescription _projectDescription;
/** - */
private List<IBundleMakerProjectChangedListener> _projectChangedListeners;
/** the state the project is in */
private BundleMakerProjectState _projectState;
/** the project description working copies */
private Map<String, ModularizedSystem> _modifiableModualizedSystemWorkingCopies;
/** - */
private List<IProblem> _problems;
/**
* <p>
* Creates a new instance of type {@link BundleMakerProject}.
* </p>
*
* @param project
* @throws Exception
*/
public BundleMakerProject(IProject project) throws CoreException {
// TODO: CoreException
Assert.isTrue(project.hasNature(Constants.NATURE_ID));
// set the project
_project = project;
// read the projectDescription
_projectDescription = loadProjectDescription();
// initialize user attributes
_userAttributes = new HashMap<String, Object>();
// create the working copies map
_modifiableModualizedSystemWorkingCopies = new HashMap<String, ModularizedSystem>();
//
_projectChangedListeners = new CopyOnWriteArrayList<IBundleMakerProjectChangedListener>();
addBundleMakerProjectChangedListener(new IBundleMakerProjectChangedListener.Adapter() {
@Override
public void projectDescriptionChanged(BundleMakerProjectDescriptionChangedEvent event) {
if (event.getType().equals(BundleMakerProjectDescriptionChangedEvent.Type.PROJECT_DESCRIPTION_RECOMPUTED)) {
BundleMakerProject.this._projectState = BundleMakerProjectState.DIRTY;
}
}
});
//
_projectState = BundleMakerProjectState.CREATED;
}
/**
* {@inheritDoc}
*/
@Override
public <T> T adaptAs(Class<T> clazz) {
//
if (clazz.isAssignableFrom(this.getClass())) {
return (T) this;
}
//
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Object getAdapter(Class adapter) {
return adaptAs(adapter);
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, Object> getUserAttributes() {
return _userAttributes;
}
/**
* {@inheritDoc}
*/
@Override
public BundleMakerProjectState getState() {
return _projectState;
}
/**
* {@inheritDoc}
*/
@Override
public void addBundleMakerProjectChangedListener(IBundleMakerProjectChangedListener listener) {
//
if (!_projectChangedListeners.contains(listener)) {
_projectChangedListeners.add(listener);
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeBundleMakerProjectChangedListener(IBundleMakerProjectChangedListener listener) {
//
if (_projectChangedListeners.contains(listener)) {
_projectChangedListeners.remove(listener);
}
}
/**
* {@inheritDoc}
*/
public void initialize(IProgressMonitor progressMonitor) throws CoreException {
// reload the project description
_projectDescription = loadProjectDescription();
_projectDescription.initialize(progressMonitor, this);
// set the initialized flag
_projectState = BundleMakerProjectState.INITIALIZED;
// notify listeners
fireProjectStateChangedEvent(new BundleMakerProjectStateChangedEvent(this, _projectState));
}
@Override
public void parseAndOpen(IProgressMonitor progressMonitor) throws CoreException {
// assert
assertState(BundleMakerProjectState.INITIALIZED, BundleMakerProjectState.READY);
// clear the modularized system working copies
// TODO
_modifiableModualizedSystemWorkingCopies.clear();
// get the store
IPersistentDependencyStoreFactory factory = XYZService.instance().getPersistentDependencyStoreFactory();
IPersistentDependencyStore store = factory.getPersistentDependencyStore(this);
try {
// get the dependency store
ModelSetup modelSetup = new ModelSetup(this);
_problems = modelSetup.setup(_projectDescription.getContent(), store, progressMonitor);
// set 'READY' state
_projectState = BundleMakerProjectState.READY;
// create default working copy
IModularizedSystem modularizedSystem = hasModularizedSystemWorkingCopy(getProject().getName()) ? getModularizedSystemWorkingCopy(getProject()
.getName())
: createModularizedSystemWorkingCopy(progressMonitor, getProject().getName());
} finally {
// release the store
factory.releasePersistentDependencyStore(this);
}
// notify listeners
fireProjectStateChangedEvent(new BundleMakerProjectStateChangedEvent(this, _projectState));
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
// set the project state
_projectState = BundleMakerProjectState.DISPOSED;
// notify listeners
fireProjectStateChangedEvent(new BundleMakerProjectStateChangedEvent(this, _projectState));
//
BundleMakerProjectCache.instance().removeCachedBundleMakerProject(_project);
}
/**
* {@inheritDoc}
*/
@Override
public List<IProblem> getProblems() {
//
List<IProblem> emptyList = Collections.emptyList();
//
return _problems == null ? emptyList : _problems;
}
// /**
// * <p>
// * </p>
// *
// * @return
// */
// @Override
// public final List<IModuleResource> getSourceResources() {
// return _projectDescription.getSourceResources();
// }
//
// /**
// * <p>
// * </p>
// *
// * @return
// */
// @Override
// public final List<IModuleResource> getBinaryResources() {
// return _projectDescription.getBinaryResources();
// }
// /**
// * <p>
// * </p>
// *
// * @return
// */
// public final List<IResourceStandin> getSourceResourceStandins() {
// return _projectDescription.getSourceResourceStandins();
// }
//
// /**
// * <p>
// * </p>
// *
// * @return
// */
// public final List<IResourceStandin> getBinaryResourceStandins() {
// return _projectDescription.getBinaryResourceStandins();
// }
/**
* {@inheritDoc}
*/
@Override
public Collection<IModularizedSystem> getModularizedSystemWorkingCopies() throws CoreException {
Collection<? extends IModularizedSystem> result = _modifiableModualizedSystemWorkingCopies.values();
return Collections.unmodifiableCollection(result);
}
/**
* {@inheritDoc}
*
* @throws CoreException
*/
@Override
public IModularizedSystem createModularizedSystemWorkingCopy(IProgressMonitor progressMonitor, String name)
throws CoreException {
// assert
Assert.isNotNull(name);
Assert.isTrue(name.trim().length() > 0);
assertState(BundleMakerProjectState.READY);
//
if (_modifiableModualizedSystemWorkingCopies.containsKey(name)) {
// TODO
throw new IllegalStateException("Modularized system '" + name + "' already registered");
}
// create the modularized system
ModularizedSystem modularizedSystem = new ModularizedSystem(name, this);
// add the result to the hash map
_modifiableModualizedSystemWorkingCopies.put(name, modularizedSystem);
// create the default transformation
ITransformation basicContentTransformation = new BasicProjectContentTransformation();
modularizedSystem.initialize(null);
modularizedSystem.applyTransformations(null, basicContentTransformation);
// *****************************************************//
// // TESTS
// for (IModuleResource moduleResource : getBinaryResources()) {
// IMovableUnit movableUnit = moduleResource.getMovableUnit();
// Assert.isNotNull(movableUnit);
// Assert.isNotNull(movableUnit.getAssociatedBinaryResources());
// Assert.isTrue(!movableUnit.getAssociatedBinaryResources().isEmpty());
// Assert.isNotNull(modularizedSystem.getAssociatedResourceModule(moduleResource));
// }
// for (IModuleResource moduleResource : getSourceResources()) {
// IMovableUnit movableUnit = moduleResource.getMovableUnit();
// Assert.isNotNull(movableUnit);
// // Assert.isNotNull(movableUnit.getAssociatedBinaryResources());
// Assert.isNotNull(movableUnit.getAssociatedSourceResource());
// Assert.isNotNull(modularizedSystem.getAssociatedResourceModule(moduleResource));
// }
// *****************************************************//
// invoke hook if available
/**
* Returns an instance of {@link IBundleMakerProjectHook} or null if no hook is registered
*
* @return
*/
// TODO
ServiceTracker<IBundleMakerProjectHook, IBundleMakerProjectHook> _projectHookTracker = new ServiceTracker<IBundleMakerProjectHook, IBundleMakerProjectHook>(
Activator.getDefault().getContext(),
IBundleMakerProjectHook.class, null);
_projectHookTracker.open();
IBundleMakerProjectHook projectHook = _projectHookTracker.getService();
_projectHookTracker.close();
if (projectHook != null) {
try {
projectHook.modularizedSystemCreated(
new NullProgressMonitor(), // TODO
modularizedSystem);
} catch (CoreException coreException) {
throw coreException;
} catch (Exception ex) {
// TODO: log exception instead of re-throw?
throw new CoreException(new Status(IStatus.ERROR, Constants.BUNDLE_ID_BUNDLEMAKER_CORE,
"BundleMaker project hook failed: " + ex, ex));
}
}
//
return modularizedSystem;
}
/**
* {@inheritDoc}
*
* @throws CoreException
*/
@Override
public boolean hasModularizedSystemWorkingCopy(String name) throws CoreException {
// assert
assertState(BundleMakerProjectState.READY);
return _modifiableModualizedSystemWorkingCopies.containsKey(name);
}
/**
* {@inheritDoc}
*
* @throws CoreException
*/
@Override
public IModularizedSystem getModularizedSystemWorkingCopy(String name) throws CoreException {
// assert
assertState(BundleMakerProjectState.READY);
if (!hasModularizedSystemWorkingCopy(name)) {
// TODO
throw new RuntimeException(name);
}
return _modifiableModualizedSystemWorkingCopies.get(name);
}
/**
* {@inheritDoc}
*/
@Override
public IModularizedSystem getModularizedSystemWorkingCopy() throws CoreException {
return getModularizedSystemWorkingCopy(getProject().getName());
}
/**
* {@inheritDoc}
*
* @throws CoreException
*/
@Override
public void deleteModularizedSystemWorkingCopy(String name) throws CoreException {
// assert
assertState(BundleMakerProjectState.READY);
if (_modifiableModualizedSystemWorkingCopies.containsKey(name)) {
_modifiableModualizedSystemWorkingCopies.remove(name);
}
}
/**
* {@inheritDoc}
*/
public IProject getProject() {
return _project.getProject();
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return getProject().getName();
}
/**
* <p>
* </p>
*
* @return
*/
@Override
public IProjectDescription getProjectDescription() {
return _projectDescription;
}
@Override
public IModifiableProjectDescription getModifiableProjectDescription() {
return _projectDescription;
}
/*
* (non-Javadoc)
*
* @see org.bundlemaker.core.IBundleMakerProject#reloadProjectDescription()
*/
@Override
public void reloadProjectDescription() throws CoreException {
//
_projectDescription = loadProjectDescription();
//
fireDescriptionChangedEvent(new BundleMakerProjectDescriptionChangedEvent(this,
BundleMakerProjectDescriptionChangedEvent.Type.PROJECT_DESCRIPTION_RELOADED));
}
@Override
public void modifyBundleMakerProjectDescription(IProjectDescriptionModifier modifier) throws CoreException {
//
Assert.isNotNull(modifier);
// Creating the project description
IModifiableProjectDescription projectDescription = this.getModifiableProjectDescription();
modifier.modifyProjectDescription(projectDescription);
projectDescription.save();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_project == null) ? 0 : _project.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BundleMakerProject other = (BundleMakerProject) obj;
if (_project == null) {
if (other._project != null)
return false;
} else if (!_project.equals(other._project))
return false;
return true;
}
/**
* <p>
* </p>
*
* @param event
*/
public void fireContentChangedEvent(BundleMakerProjectContentChangedEvent event) {
Assert.isNotNull(event);
//
for (IBundleMakerProjectChangedListener listener : _projectChangedListeners) {
listener.projectContentChanged(event);
}
}
/**
* <p>
* </p>
*
* @param event
*/
public void fireDescriptionChangedEvent(BundleMakerProjectDescriptionChangedEvent event) {
Assert.isNotNull(event);
//
for (IBundleMakerProjectChangedListener listener : _projectChangedListeners) {
listener.projectDescriptionChanged(event);
}
}
/**
* <p>
* </p>
*
* @param event
*/
public void fireProjectStateChangedEvent(BundleMakerProjectStateChangedEvent event) {
Assert.isNotNull(event);
//
for (IBundleMakerProjectChangedListener listener : _projectChangedListeners) {
listener.projectStateChanged(event);
}
}
/**
* <p>
* </p>
*
* @return
* @throws CoreException
*/
private BundleMakerProjectDescription loadProjectDescription() throws CoreException {
//
return ProjectDescriptionStore.loadProjectDescription(this);
}
/**
* <p>
* </p>
*
* @param state
* @throws CoreException
*/
private void assertState(BundleMakerProjectState... state) throws CoreException {
// check the states
for (BundleMakerProjectState aState : state) {
if (aState != null && aState.equals(getState())) {
return;
}
}
// throw new exception
throw new CoreException(new Status(IStatus.ERROR, Constants.BUNDLE_ID_BUNDLEMAKER_CORE, String.format(
"BundleMakerProject must be in one of the following states: '%s', but current state is '%s'.",
Arrays.asList(state), getState())));
}
}