eclipse plugin: Big commit. Further enhance the builder...

...so it can update modified files as well. It now works to change the
order of the includes
This commit is contained in:
Timotei Dolean 2011-07-13 20:46:43 +00:00
parent b178c62a2d
commit 3f3b96668d
4 changed files with 293 additions and 110 deletions

View file

@ -16,27 +16,18 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.wesnoth.Logger;
import org.wesnoth.utils.ListUtils;
import org.wesnoth.utils.ResourceUtils;
import org.wesnoth.utils.ResourceUtils.WMLFilesComparator;
import org.wesnoth.utils.StringUtils;
import org.wesnoth.wml.WMLMacroCall;
import org.wesnoth.wml.WMLRoot;
public class DependencyListBuilder implements Serializable
{
@ -47,21 +38,21 @@ public class DependencyListBuilder implements Serializable
*/
public static final String ROOT_NODE_KEY = "_ROOT_";
protected transient IProject project_;
private transient IProject project_;
protected boolean isCreated_;
protected int currentIndex_;
private boolean isCreated_;
private int currentIndex_;
private DependencyListNode previous_;
protected Map< String, DependencyListNode > list_;
private Map< String, DependencyListNode > list_;
/**
* Holds a list of directories that are parsed in the WML order
* (that is, they don't have a _main.cfg in them) and
* the value is the first node in that directory existing in the list
*/
protected List< String > directories_;
protected List< ListDirectoryEntry > directoriesEntries_;
private List< String > directories_;
private List< ListDirectoryEntry > directoriesEntries_;
public DependencyListBuilder( IProject project )
{
@ -109,6 +100,8 @@ public class DependencyListBuilder implements Serializable
*/
public DependencyListNode addNode( IFile file )
{
//TODO: create better heuristics for the indexes
// in case we need to add multiple subsequent files...
DependencyListNode backupPrevious = previous_, newNode = null;
String fileParentProjectPath = file.getParent( ).getProjectRelativePath( ).toString( );
@ -118,7 +111,7 @@ public class DependencyListBuilder implements Serializable
int dirEntryIndex = directories_.indexOf( fileParentProjectPath );
ListDirectoryEntry entry = directoriesEntries_.get( dirEntryIndex );
DependencyListNode tmpNode = entry.Node;
DependencyListNode tmpNode = entry.FirstNode;
// had any files in dir?
if ( tmpNode != null ) {
@ -133,17 +126,7 @@ public class DependencyListBuilder implements Serializable
// save the previous
previous_ = tmpNode.getPrevious( );
// now delete all nodes that are in the same folder
while ( tmpNode != null &&
tmpNode.getFile( ).getParent().
getProjectRelativePath( ).toString().equals( fileParentProjectPath ) ) {
removeNode( tmpNode );
tmpNode = tmpNode.getNext( );
}
// clear the directories entry since we can't use it anymore
directories_.remove( dirEntryIndex );
directoriesEntries_.remove( dirEntryIndex );
internal_removeContainer( fileParentProjectPath );
// create the node
newNode = internal_addNode( file );
@ -179,7 +162,7 @@ public class DependencyListBuilder implements Serializable
while ( dirEntryIndex > 0 && tmpNode == null ) {
-- dirEntryIndex;
tmpNode = directoriesEntries_.get( dirEntryIndex ).Node;
tmpNode = directoriesEntries_.get( dirEntryIndex ).FirstNode;
}
previous_ = tmpNode;
@ -187,19 +170,16 @@ public class DependencyListBuilder implements Serializable
}
// now, parse the file to check if we should include other dirs
internal_addContainers( getContainers( file ) );
internal_addContainers( newNode.getIncludes( true ) );
} else {
// didn't found any place to put it. where shall we?
//TODO: the place should be dictated by other cfg,
// the place should be dictated by other cfg,
// by getting the included directory
}
// restore old previous
previous_ = backupPrevious;
// print the new list
System.out.println( toString( ) );
return newNode;
}
@ -229,10 +209,10 @@ public class DependencyListBuilder implements Serializable
IResource main_cfg = container.findMember( "_main.cfg" ); //$NON-NLS-1$
if ( main_cfg != null ) {
// add main.cfg to list
internal_addNode( (IFile) main_cfg );
DependencyListNode newNode = internal_addNode( (IFile) main_cfg );
// add any included containers
internal_addContainers( getContainers( (IFile) main_cfg ) );
internal_addContainers( newNode.getIncludes( true ) );
}else {
// no main.cfg, just follow WML reading rules
List<IResource> members = null;
@ -241,7 +221,6 @@ public class DependencyListBuilder implements Serializable
}
catch ( CoreException e ) {
Logger.getInstance( ).logException( e );
return;
}
@ -250,7 +229,7 @@ public class DependencyListBuilder implements Serializable
boolean toAddDirectoryEntry = false;
if ( ! directories_.contains( containerPath ) ) {
directories_.add( containerPath );
directoriesEntries_.add( new ListDirectoryEntry( containerPath, null ) );
directoriesEntries_.add( new ListDirectoryEntry( containerPath, null, null ) );
toAddDirectoryEntry = true;
} else {
@ -263,6 +242,7 @@ public class DependencyListBuilder implements Serializable
return;
DependencyListNode firstNewNode = null;
DependencyListNode lastNode = null;
for ( IResource resource : members ) {
if ( resource instanceof IContainer )
@ -272,29 +252,36 @@ public class DependencyListBuilder implements Serializable
if ( !ResourceUtils.isConfigFile( resource ) )
continue;
if ( firstNewNode != null )
internal_addNode( ( IFile ) resource );
else
firstNewNode = internal_addNode( (IFile) resource );
lastNode = internal_addNode( ( IFile ) resource );
if ( firstNewNode == null )
firstNewNode = lastNode;
}
}
if ( firstNewNode != null ) {
if ( firstNewNode != null && lastNode != null ) {
if ( toAddDirectoryEntry ) {
// update the first directory node
directoriesEntries_.set( directories_.size( ) - 1,
new ListDirectoryEntry( containerPath, firstNewNode ));
new ListDirectoryEntry( containerPath, firstNewNode, lastNode ));
} else {
// maybe we need to update the first dir node, if that
// should be before the current one, or it is null
// if the current entry has null nodes
// or the indexes are greater/lower than the current ones
// we need to update the references nodes
ListDirectoryEntry entry = directoriesEntries_.get(
directories_.indexOf( containerPath ) );
if ( entry.Node == null ||
( entry.Node != null &&
firstNewNode.getIndex( ) < entry.Node.getIndex( ) ) ) {
entry.Node = firstNewNode;
if ( entry.FirstNode == null ||
( entry.FirstNode != null &&
firstNewNode.getIndex( ) < entry.FirstNode.getIndex( ) ) ) {
entry.FirstNode = firstNewNode;
}
if ( entry.LastNode == null ||
( entry.LastNode != null &&
lastNode.getIndex( ) > entry.LastNode.getIndex( ) ) ) {
entry.LastNode = lastNode;
}
}
}
}
@ -312,7 +299,7 @@ public class DependencyListBuilder implements Serializable
if ( previous_ != null ){
// inserting is done between 2 nodes
if ( previous_.getNext( ) != null ){
if ( previous_.getNext( ) != null ) {
int newIndex = (previous_.getIndex( ) +
previous_.getNext( ).getIndex( )) / 2;
@ -391,79 +378,174 @@ public class DependencyListBuilder implements Serializable
// removing a _main.cfg, add the parent container
// back to the list along with it's directories_ entry
if ( node.getFile( ).getName( ).equals( "_main.cfg" ) ) {
DependencyListNode backNode = previous_;
DependencyListNode backupPrevious = previous_;
previous_ = node.getPrevious( );
internal_addContainer( fileParentProjectPath );
previous_ = backNode;
previous_ = backupPrevious;
}
//debug
System.out.println( "After removal: " + toString( ) );
}
/**
* Gets the set of included containers in this file
* as a macro call
* @param file The file to get the containers from
* @return A set of containers represented by their Path as string
* Removes the container and all it's contents from the list
* @param path The container's path
*/
public static Set<String> getContainers( IFile file )
private void internal_removeContainer( String path )
{
IProject project = file.getProject( );
WMLRoot root = ResourceUtils.getWMLRoot( file );
// nothing to do
if ( root == null )
return new LinkedHashSet<String> ( 0 );
int dirEntryIndex = directories_.indexOf( path );
if ( dirEntryIndex == -1 )
return;
EList<WMLMacroCall> macroCalls = new BasicEList<WMLMacroCall>( );
ListDirectoryEntry entry = directoriesEntries_.get( dirEntryIndex );
// iterate to find macro calls
TreeIterator<EObject> treeItor = root.eAllContents( );
if ( entry == null )
return;
while ( treeItor.hasNext( ) ) {
EObject object = treeItor.next( );
if ( object instanceof WMLMacroCall ){
macroCalls.add( (WMLMacroCall) object );
}
-- entry.Includes;
// we shouldn't delete entries that are used more than 1 time
if ( entry.Includes > 0 )
return;
DependencyListNode firstNode = entry.FirstNode;
DependencyListNode lastNode = entry.LastNode.getNext( );
// now delete all nodes that are in the same folder
while ( firstNode != null && firstNode != lastNode) {
removeNode( firstNode );
firstNode = firstNode.getNext( );
}
// now check what macros are really an inclusion macro
Set<String> containersToAdd = new LinkedHashSet<String>( );
// clear the directories entry since we can't use it anymore
directories_.remove( dirEntryIndex );
directoriesEntries_.remove( dirEntryIndex );
}
for ( WMLMacroCall macro : macroCalls ) {
String name = macro.getName( );
/**
* Updates the current node in the list
* @param node The node to update
*/
public void updateNode( DependencyListNode node )
{
// check the includes, to see if they changed
List<String> previousIncludes = node.getIncludes( false );
int prevLength = previousIncludes.size( );
/**
* To include a folder the macro should be the following
* forms:
* - {campaigns/... }
* - {~add-ons/... }
*
*/
//TODO: check for including a specific config file?
List<String> newIncludes = node.getIncludes( true );
int newLength = newIncludes.size( );
if ( ( name.equals( "campaigns" ) || //$NON-NLS-1$
name.equals( "add-ons" ) ) && //$NON-NLS-1$
// the call should contain just string values
macro.getExtraMacros( ).isEmpty( ) &&
macro.getParams( ).size( ) > 2 &&
macro.getParams( ).get( 0 ).equals( "/" ) ) //$NON-NLS-1$
{
// check if the macro includes directories local
// to this project
String projectPath = project.getLocation( ).toOSString( );
List<String> processedIncludes = new ArrayList<String>();
if ( projectPath.contains( macro.getParams( ).get( 1 ) ) ) {
containersToAdd.add(
ListUtils.concatenateList(
macro.getParams( ).subList( 3, macro.getParams( ).size( ) ), "" ) ); //$NON-NLS-1$
for ( int prevIndex = 0, newIndex = 0
; prevIndex < prevLength && newIndex < newLength; ) {
String prevIncl = previousIncludes.get( prevIndex );
String newIncl = newIncludes.get( prevIndex );
// nothing changed
if ( prevIncl.equals( newIncl ) ) {
++ prevIndex;
++ newIndex;
continue;
}
boolean newIsNew = !previousIncludes.contains( newIncl );
boolean prevDeleted = !newIncludes.contains( prevIncl );
if ( newIsNew ) {
// add the new directory before the previous included dir (if any)
DependencyListNode backupPrevious = previous_;
previous_ = null;
// get the directory entry for the previous include
if ( newIndex > 0 ) {
ListDirectoryEntry entry = directoriesEntries_.get(
directories_.indexOf( newIncludes.get( newIndex - 1 ) ) );
if ( entry != null )
previous_ = entry.FirstNode;
}
internal_addContainer( newIncl );
previous_ = backupPrevious;
++newIndex;
} else {
if ( prevDeleted ) {
// the previous include was deleted
internal_removeContainer( prevIncl );
} else {
// the previous included has changed it's index (in the includes list)
// don't reprocess this pair if we already did
if ( !processedIncludes.contains( prevIncl ) ) {
ListDirectoryEntry prevEntry = directoriesEntries_.get(
directories_.indexOf( prevIncl ) );
ListDirectoryEntry newEntry = directoriesEntries_.get(
directories_.indexOf( newIncl ) );
if ( prevEntry != null && newEntry != null &&
prevEntry.FirstNode != null && newEntry.FirstNode != null) {
// create a list for easier swap
List<DependencyListNode> nodes = new ArrayList<DependencyListNode>();
nodes.add( prevEntry.FirstNode.getPrevious( ) );
nodes.add( prevEntry.FirstNode );
nodes.add( prevEntry.LastNode );
if ( prevEntry.LastNode.getNext( ) != newEntry.FirstNode ) {
nodes.add( prevEntry.LastNode.getNext( ) );
nodes.add( newEntry.FirstNode.getPrevious( ) );
}
nodes.add( newEntry.FirstNode );
nodes.add( newEntry.LastNode );
nodes.add( newEntry.LastNode.getNext( ) );
int nodesSize = nodes.size( );
int swapIndex = ( nodesSize == 8 ? 5 : 3 );
// now swap the nodes
DependencyListNode tmp = nodes.get( swapIndex );
nodes.set( swapIndex, nodes.get( 1 ) );
nodes.set( 1, tmp );
swapIndex = ( nodesSize == 8 ? 6 : 4 );
tmp = nodes.get( swapIndex );
nodes.set( swapIndex, nodes.get( 2 ) );
nodes.set( 2, tmp );
// update the links
for ( int i = 0; i < nodesSize - 1; i += 2 ) {
DependencyListNode fst = nodes.get( i );
DependencyListNode lst = nodes.get( i + 1 );
if ( fst != null )
fst.setNext( lst );
if ( lst != null )
lst.setPrevious( fst );
}
processedIncludes.add( prevIncl );
processedIncludes.add( newIncl );
} else {
Logger.getInstance( ).log( "Null directory entry for" +
"includes: " + prevIncl + " and " + newIncl );
}
}
}
++newIndex;
++prevIndex;
}
}
return containersToAdd;
System.out.println( toString( ) );
}
/**
@ -558,17 +640,24 @@ public class DependencyListBuilder implements Serializable
/**
* The first node in this directory existing in the list
*/
public DependencyListNode Node;
public DependencyListNode FirstNode;
/**
* The last node in this directory existing in the list
*/
public DependencyListNode LastNode;
/**
* Numbers of times this directory is included.
*/
public int Includes;
public ListDirectoryEntry( String name, DependencyListNode node)
public ListDirectoryEntry( String name, DependencyListNode firstNode,
DependencyListNode lastNode )
{
Name = name;
Node = node;
FirstNode = firstNode;
LastNode = lastNode;
Includes = 1;
}
}

View file

@ -17,6 +17,7 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.wesnoth.Constants;
import org.wesnoth.Logger;
import org.wesnoth.utils.ResourceUtils;
/**
* This class represents a node in the Project's Depedency list,
@ -34,9 +35,9 @@ public class DependencyListNode implements Serializable
* Since int it's on 4 bytes, it can hold values between
* -2,147,483,648 and 2,147,483,647.
*
* With an increment of 10k, we could have 2*214,748 config files.
* With an increment of 100k, we could have 2 * 21,474 config files.
*/
public static final int INDEX_STEP = 10000;
public static final int INDEX_STEP = 100000;
protected static final QualifiedName PDL_INDEX = new QualifiedName( Constants.PLUGIN_ID, "pdl_index" ); //$NON-NLS-1$
@ -69,6 +70,21 @@ public class DependencyListNode implements Serializable
return file_;
}
/**
* Gets the includes from this node
* @param refresh True to force reloading the current file and return
* the newly parsed ones
* @return A set with string paths for included directories
*/
public List<String> getIncludes ( boolean refresh )
{
if ( includes_ == null || refresh ) {
includes_ = new ArrayList<String>( ResourceUtils.getContainers( file_ ) );
}
return includes_;
}
/**
* Returns the index of this node in the whole dependency list
* @return

View file

@ -52,8 +52,6 @@ import org.wesnoth.wml.core.ConfigFile;
* 2) parse ADDED or CHANGED files, to check if new directory/file includes
* happened
* 3) add new dependencies to the list
*
//TODO: what happens if a file changes the order of the includes?
*/
public class WesnothProjectBuilder extends IncrementalProjectBuilder
{
@ -90,11 +88,14 @@ public class WesnothProjectBuilder extends IncrementalProjectBuilder
WorkspaceUtils.getTemporaryFolder();
monitor.worked(2);
if ( runAntJob(paths, monitor ) == false )
return null;
boolean readDefines = false;
monitor.subTask( "Build started..." );
if (kind == FULL_BUILD)
readDefines = fullBuild(monitor);
else
@ -185,20 +186,27 @@ public class WesnothProjectBuilder extends IncrementalProjectBuilder
if ( deltaKind == IResourceDelta.REMOVED ) {
projectCache_.getDependencyList( ).removeNode( file );
projectCache_.getConfigs().remove( file.getProjectRelativePath( ).toString( ) );
//debug
System.out.println( "After removal: " + list.toString( ) );
} else if ( deltaKind == IResourceDelta.ADDED ){
DependencyListNode newNode = list.addNode( file );
if ( newNode == null )
Logger.getInstance( ).logError( "Couldn't create a new" +
"PDL node for file: " + file.getFullPath( ).toString( ) );
else
else {
System.out.println( list.toString( ) );
nodesToProcess.add( newNode );
}
} else if ( deltaKind == IResourceDelta.CHANGED ) {
DependencyListNode node = list.getNode( file );
if ( node == null )
Logger.getInstance( ).logError( "Couldn't find file "
+ file.getFullPath( ).toString( ) + " in PDL!." );
else
else {
nodesToProcess.add( node );
list.updateNode( node );
}
} else {
Logger.getInstance( ).log( "unknown delta kind: " + deltaKind );
}

View file

@ -20,8 +20,10 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
@ -36,6 +38,9 @@ import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
@ -50,6 +55,7 @@ import org.wesnoth.preprocessor.PreprocessorUtils;
import org.wesnoth.projects.ProjectUtils;
import org.wesnoth.templates.ReplaceableParameter;
import org.wesnoth.templates.TemplateProvider;
import org.wesnoth.wml.WMLMacroCall;
import org.wesnoth.wml.WMLRoot;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@ -531,6 +537,70 @@ public class ResourceUtils
return ( WMLRoot ) result;
}
/**
* Gets the set of included containers in this file
* as a macro call
* @param file The file to get the containers from
* @return A set of containers represented by their Path as string
*/
public static Set<String> getContainers( IFile file )
{
IProject project = file.getProject( );
WMLRoot root = ResourceUtils.getWMLRoot( file );
// nothing to do
if ( root == null )
return new LinkedHashSet<String> ( 0 );
EList<WMLMacroCall> macroCalls = new BasicEList<WMLMacroCall>( );
// iterate to find macro calls
TreeIterator<EObject> treeItor = root.eAllContents( );
while ( treeItor.hasNext( ) ) {
EObject object = treeItor.next( );
if ( object instanceof WMLMacroCall ){
macroCalls.add( (WMLMacroCall) object );
}
}
// now check what macros are really an inclusion macro
Set<String> containersToAdd = new LinkedHashSet<String>( );
for ( WMLMacroCall macro : macroCalls ) {
String name = macro.getName( );
/**
* To include a folder the macro should be the following
* forms:
* - {campaigns/... }
* - {~add-ons/... }
*
*/
//TODO: check for including a specific config file?
if ( ( name.equals( "campaigns" ) || //$NON-NLS-1$
name.equals( "add-ons" ) ) && //$NON-NLS-1$
// the call should contain just string values
macro.getExtraMacros( ).isEmpty( ) &&
macro.getParams( ).size( ) > 2 &&
macro.getParams( ).get( 0 ).equals( "/" ) ) //$NON-NLS-1$
{
// check if the macro includes directories local
// to this project
String projectPath = project.getLocation( ).toOSString( );
if ( projectPath.contains( macro.getParams( ).get( 1 ) ) ) {
containersToAdd.add(
ListUtils.concatenateList(
macro.getParams( ).subList( 3, macro.getParams( ).size( ) ), "" ) ); //$NON-NLS-1$
}
}
}
return containersToAdd;
}
/**
* This is a WML files comparator, based on the WML parsing rules.
*