/**********************************************************************
* Copyright (c) 2005-2009 ant4eclipse project team.
*
* 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:
* Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich
**********************************************************************/
package org.ant4eclipse.lib.pydt.internal.tools;
import org.ant4eclipse.lib.core.Assure;
import org.ant4eclipse.lib.core.exception.Ant4EclipseException;
import org.ant4eclipse.lib.core.logging.A4ELogging;
import org.ant4eclipse.lib.core.service.ServiceRegistryAccess;
import org.ant4eclipse.lib.platform.model.resource.EclipseProject;
import org.ant4eclipse.lib.platform.model.resource.Workspace;
import org.ant4eclipse.lib.pydt.PydtExceptionCode;
import org.ant4eclipse.lib.pydt.internal.model.project.PythonProjectRole;
import org.ant4eclipse.lib.pydt.model.RawPathEntry;
import org.ant4eclipse.lib.pydt.model.ReferenceKind;
import org.ant4eclipse.lib.pydt.model.ResolvedContainerEntry;
import org.ant4eclipse.lib.pydt.model.ResolvedLibraryEntry;
import org.ant4eclipse.lib.pydt.model.ResolvedPathEntry;
import org.ant4eclipse.lib.pydt.model.ResolvedProjectEntry;
import org.ant4eclipse.lib.pydt.model.ResolvedRuntimeEntry;
import org.ant4eclipse.lib.pydt.model.ResolvedSourceEntry;
import org.ant4eclipse.lib.pydt.model.pyre.PythonRuntime;
import org.ant4eclipse.lib.pydt.model.pyre.PythonRuntimeRegistry;
import org.ant4eclipse.lib.pydt.tools.PathEntryRegistry;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* General resolved for python.
*
* @author Daniel Kasmeroglu (Daniel.Kasmeroglu@Kasisoft.net)
*/
public class PythonResolver {
public enum Mode {
all, exported, direct
}
private PathEntryRegistry _pathregistry;
private PythonRuntimeRegistry _runtimeregistry;
private Workspace _workspace;
private Mode _mode;
private boolean _ignoreruntimes;
/**
* Initialises this resolver.
*
* @param workspace
* The Workspace instance currently used for the resolving. Not <code>null</code>.
* @param mode
* The resolving mode. <code>null</code> means Mode#all.
* @param ignoreruntimes
* <code>true</code> <=> Runtimes have to be ignored.
*/
public PythonResolver(Workspace workspace, Mode mode, boolean ignoreruntimes) {
Assure.notNull("workspace", workspace);
this._pathregistry = ServiceRegistryAccess.instance().getService(PathEntryRegistry.class);
this._runtimeregistry = ServiceRegistryAccess.instance().getService(PythonRuntimeRegistry.class);
this._workspace = workspace;
this._ignoreruntimes = ignoreruntimes;
this._mode = mode;
if (this._mode == null) {
this._mode = Mode.all;
}
}
/**
* Resolves the supplied entry to get access to a source folder.
*
* @param entry
* The unresolved entry pointing to a source folder. Not <code>null</code>.
*/
private void resolveImpl(RawPathEntry entry) {
Assure.notNull("entry", entry);
ResolvedPathEntry result = this._pathregistry.getResolvedPathEntry(entry);
if (result == null) {
result = newResolvedEntry(entry);
}
if (result != null) {
this._pathregistry.registerResolvedPathEntry(entry, result);
}
}
/**
* Resolves the supplied entries to get access to the source folders.
*
* @param entries
* The unresolved entries pointing to the source folders. Not <code>null</code>.
*
* @return The resolved entries identifying the source folders. Not <code>null</code>.
*/
public ResolvedPathEntry[] resolve(String projectname) {
EclipseProject project = this._workspace.getProject(projectname);
return resolve(loadEntries(project));
}
/**
* Resolves the supplied entries to get access to the source folders.
*
* @param entries
* The unresolved entries pointing to the source folders. Not <code>null</code>.
*
* @return The resolved entries identifying the source folders. Not <code>null</code>.
*/
public ResolvedPathEntry[] resolve(RawPathEntry... entries) {
Assure.notNull("entries", entries);
List<RawPathEntry> input = new ArrayList<RawPathEntry>();
for (RawPathEntry entry : entries) {
input.add(entry);
}
return resolve(input);
}
/**
* Resolves the supplied entries to get access to the source folders.
*
* @param entries
* The unresolved entries pointing to the source folders. Not <code>null</code>.
*
* @return The resolved entries identifying the source folders. Not <code>null</code>.
*/
public ResolvedPathEntry[] resolve(List<RawPathEntry> entries) {
Assure.notNull("entries", entries);
List<ResolvedPathEntry> list = new ArrayList<ResolvedPathEntry>();
resolve(list, filter(entries));
return list.toArray(new ResolvedPathEntry[list.size()]);
}
/**
* Filters entries that don't need to be processed due to the current setup.
*
* @param input
* A list of entries that need to be filtered. Not <code>null</code>.
*
* @return A list only containing the entries that need to be processed. Not <code>null</code>.
*/
private List<RawPathEntry> filter(List<RawPathEntry> input) {
if (this._ignoreruntimes) {
List<RawPathEntry> result = new ArrayList<RawPathEntry>();
for (int i = 0; i < input.size(); i++) {
if (input.get(i).getKind() != ReferenceKind.Runtime) {
result.add(input.get(i));
}
}
return result;
} else {
return input;
}
}
/**
* Performs the actual resolving process.
*
* @param receiver
* The list used to collect all resolved entries. Not <code>null</code>.
* @param entries
* The entries that need to be processed. Not <code>null</code>.
*/
private void resolve(List<ResolvedPathEntry> receiver, List<RawPathEntry> entries) {
Set<RawPathEntry> followed = new HashSet<RawPathEntry>();
while (!entries.isEmpty()) {
RawPathEntry entry = entries.remove(0);
if (!this._pathregistry.isResolved(entry)) {
// until now it has not been resolved, so resolve and register it
resolveImpl(entry);
}
// access the resolved path entry
ResolvedPathEntry resolved = this._pathregistry.getResolvedPathEntry(entry);
if (!receiver.contains(resolved)) {
receiver.add(resolved);
}
if (!canBeFollowed(resolved)) {
// the entry cannot be refined so there's no more to do
continue;
}
if (followed.contains(entry)) {
// this one already has been followed
continue;
}
followed.add(entry);
if (this._mode == Mode.direct) {
// we're not interested in indirectly used entries
continue;
}
if (resolved.getKind() == ReferenceKind.Project) {
// fetch the EclipseProject instance from the workspace
EclipseProject refproject = this._workspace.getProject(((ResolvedProjectEntry) resolved).getProjectname());
if (this._mode == Mode.all) {
// this mode doesn't care for the 'export' flag on path settings
entries.addAll(0, loadEntries(refproject));
} else if ((this._mode == Mode.exported) && entry.isExported()) {
// just follow exported entries
entries.addAll(0, loadEntries(refproject));
}
}
}
}
/**
* Returns a list of raw path entries from the supplied project.
*
* @param project
* The project which raw path entries have to be returned. Not <code>null</code>.
*
* @return A list of raw path entries. Not <code>null</code>.
*/
private List<RawPathEntry> loadEntries(EclipseProject project) {
List<RawPathEntry> result = new ArrayList<RawPathEntry>();
PythonProjectRole role = project.getRole(PythonProjectRole.class);
RawPathEntry[] entries = role.getRawPathEntries();
for (RawPathEntry entry : entries) {
if (entry.getKind() == ReferenceKind.Runtime) {
if (this._ignoreruntimes) {
continue;
}
}
result.add(entry);
}
return result;
}
/**
* Returns <code>true</code> if the supplied entry can be refined.
*
* @param entry
* The entry that can be refined. Not <code>null</code>.
*
* @return <code>true</code> <=> The entry could be refined.
*/
private boolean canBeFollowed(ResolvedPathEntry entry) {
return entry.getKind() == ReferenceKind.Project;
}
/**
* Creates a new resolved record for the supplied entry.
*
* @param entry
* The path entry which needs to be resolved. Not <code>null</code>.
*/
private ResolvedPathEntry newResolvedEntry(RawPathEntry entry) {
if (entry.getKind() == ReferenceKind.Container) {
return newResolvedContainerEntry(entry);
} else if (entry.getKind() == ReferenceKind.Library) {
return newResolvedLibraryEntry(entry);
} else if (entry.getKind() == ReferenceKind.Project) {
return newResolvedProjectEntry(entry);
} else if (entry.getKind() == ReferenceKind.Runtime) {
return newResolvedRuntimeEntry(entry);
} else /* if (entry.getKind() == ReferenceKind.Source) */{
return newResolvedSourceEntry(entry);
}
}
/**
* Creates a new record representing a path container.
*
* @param entry
* The raw entry. Not <code>null</code>.
*/
private ResolvedContainerEntry newResolvedContainerEntry(RawPathEntry entry) {
return new ResolvedContainerEntry(entry.getProjectname(), new File[0]);
}
/**
* Creates a new record representing a library.
*
* @param entry
* The raw entry. Not <code>null</code>.
*/
private ResolvedLibraryEntry newResolvedLibraryEntry(RawPathEntry entry) {
return new ResolvedLibraryEntry(entry.getProjectname(), entry.getValue());
}
/**
* Creates a new record representing a project.
*
* @param entry
* The raw entry. Not <code>null</code>.
*/
private ResolvedProjectEntry newResolvedProjectEntry(RawPathEntry entry) {
String value = entry.getValue();
if ((value.charAt(0) != '/') || (value.length() == 1)) {
/** @todo [02-Aug-2009:KASI] We need to cause an exception here. */
A4ELogging.warn("The raw projectname '%s' does not start conform to the required format '/' <identifier> !",
value);
return null;
}
return new ResolvedProjectEntry(entry.getProjectname(), value.substring(1));
}
/**
* Creates a new record representing a source folder.
*
* @param entry
* The raw entry. Not <code>null</code>.
*/
private ResolvedSourceEntry newResolvedSourceEntry(RawPathEntry entry) {
return new ResolvedSourceEntry(entry.getProjectname(), entry.getValue());
}
/**
* Creates a new record representing a single runtime..
*
* @param entry
* The raw entry. Not <code>null</code>.
*/
private ResolvedRuntimeEntry newResolvedRuntimeEntry(RawPathEntry entry) {
String value = entry.getValue();
PythonRuntime runtime = null;
if (value.length() == 0) {
// use the default runtime
runtime = this._runtimeregistry.getRuntime();
} else {
// use the selected runtime
runtime = this._runtimeregistry.getRuntime(value);
}
if (runtime == null) {
// now runtime available
throw new Ant4EclipseException(PydtExceptionCode.UNKNOWN_PYTHON_RUNTIME, value);
}
return new ResolvedRuntimeEntry(entry.getProjectname(), runtime.getVersion(), runtime.getLibraries());
}
} /* ENDCLASS */