Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 194917 | Differences between
and this patch

Collapse All | Expand All

(-)Eclipse UI/org/eclipse/ui/internal/WorkbenchMessages.java (-1 / +2 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2005, 2008 IBM Corporation and others.
2
 * Copyright (c) 2005, 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 816-821 Link Here
816
	public static String ProblemRestoringWorkingSetState_message;
816
	public static String ProblemRestoringWorkingSetState_message;
817
817
818
	public static String ProblemRestoringWorkingSetState_title;
818
	public static String ProblemRestoringWorkingSetState_title;
819
	public static String ProblemCyclicDependency;
819
820
820
	public static String WorkingSetEditWizard_title;
821
	public static String WorkingSetEditWizard_title;
821
	public static String WorkingSetNewWizard_title;
822
	public static String WorkingSetNewWizard_title;
(-)Eclipse UI/org/eclipse/ui/internal/messages.properties (+1 lines)
Lines 787-792 Link Here
787
ProblemSavingWorkingSetState_title = Saving Problems
787
ProblemSavingWorkingSetState_title = Saving Problems
788
ProblemRestoringWorkingSetState_message = Unable to restore working set state.
788
ProblemRestoringWorkingSetState_message = Unable to restore working set state.
789
ProblemRestoringWorkingSetState_title = Restoring Problems
789
ProblemRestoringWorkingSetState_title = Restoring Problems
790
ProblemCyclicDependency = Removed cyclic dependency in the working set ''{0}''.
790
791
791
WorkingSetEditWizard_title=Edit Working Set
792
WorkingSetEditWizard_title=Edit Working Set
792
WorkingSetNewWizard_title=New Working Set
793
WorkingSetNewWizard_title=New Working Set
(-)Eclipse UI/org/eclipse/ui/internal/AggregateWorkingSet.java (-12 / +43 lines)
Lines 19-24 Link Here
19
import org.eclipse.jface.resource.ImageDescriptor;
19
import org.eclipse.jface.resource.ImageDescriptor;
20
import org.eclipse.jface.util.IPropertyChangeListener;
20
import org.eclipse.jface.util.IPropertyChangeListener;
21
import org.eclipse.jface.util.PropertyChangeEvent;
21
import org.eclipse.jface.util.PropertyChangeEvent;
22
import org.eclipse.osgi.util.NLS;
22
import org.eclipse.ui.IAggregateWorkingSet;
23
import org.eclipse.ui.IAggregateWorkingSet;
23
import org.eclipse.ui.IMemento;
24
import org.eclipse.ui.IMemento;
24
import org.eclipse.ui.IWorkingSet;
25
import org.eclipse.ui.IWorkingSet;
Lines 35-40 Link Here
35
	private IWorkingSet[] components;
36
	private IWorkingSet[] components;
36
37
37
	/**
38
	/**
39
	 * Prevents stack overflow on cyclic element inclusions.
40
	 */
41
	private boolean inElementConstruction = false;
42
43
	/**
38
	 * 
44
	 * 
39
	 * @param name
45
	 * @param name
40
	 * @param label
46
	 * @param label
Lines 77-93 Link Here
77
	 * @param fireEvent whether a working set change event should be fired
83
	 * @param fireEvent whether a working set change event should be fired
78
	 */
84
	 */
79
	private void constructElements(boolean fireEvent) {
85
	private void constructElements(boolean fireEvent) {
80
		Set elements = new HashSet();
86
		if (inElementConstruction) {
81
		IWorkingSet[] localComponents = getComponentsInternal();
87
			String msg = NLS.bind(WorkbenchMessages.ProblemCyclicDependency, getName());
82
		for (int i = 0; i < localComponents.length; i++) {
88
            WorkbenchPlugin.log(msg);
83
			IWorkingSet workingSet = localComponents[i];
89
            throw new IllegalStateException(msg);
84
			elements.addAll(Arrays.asList(workingSet.getElements()));
90
		}
85
		}
91
		inElementConstruction = true;
86
		internalSetElements((IAdaptable[]) elements
92
		try {
87
				.toArray(new IAdaptable[elements.size()]));
93
			Set elements = new HashSet();
88
		if (fireEvent) {
94
			IWorkingSet[] localComponents = getComponentsInternal();
89
			fireWorkingSetChanged(
95
			for (int i = 0; i < localComponents.length; i++) {
90
				IWorkingSetManager.CHANGE_WORKING_SET_CONTENT_CHANGE, null);
96
				IWorkingSet workingSet = localComponents[i];
97
				try {
98
					IAdaptable[] componentElements = workingSet.getElements();
99
					elements.addAll(Arrays.asList(componentElements));
100
				} catch (IllegalStateException e) { // an invalid component; remove it
101
					IWorkingSet[] tmp = new IWorkingSet[components.length - 1];
102
					if (i > 0)
103
						System.arraycopy(components, 0, tmp, 0, i);
104
					if (components.length - i - 1 > 0)
105
						System.arraycopy(components, i + 1, tmp, i, components.length - i - 1);
106
					components = tmp;
107
					workingSetMemento = null; // toss cached info
108
					fireWorkingSetChanged(IWorkingSetManager.CHANGE_WORKING_SET_CONTENT_CHANGE, null);						
109
					continue;
110
				}
111
			}
112
			internalSetElements((IAdaptable[]) elements
113
					.toArray(new IAdaptable[elements.size()]));
114
			if (fireEvent) {
115
				fireWorkingSetChanged(
116
					IWorkingSetManager.CHANGE_WORKING_SET_CONTENT_CHANGE, null);
117
			}
118
		} finally {
119
			inElementConstruction = false;
91
		}
120
		}
92
	}
121
	}
93
122
Lines 150-156 Link Here
150
	}
179
	}
151
180
152
	public void disconnect() {
181
	public void disconnect() {
153
		getManager().removePropertyChangeListener(this);
182
		IWorkingSetManager connectedManager = getManager();
183
		if (connectedManager != null)
184
			connectedManager.removePropertyChangeListener(this);
154
		super.disconnect();
185
		super.disconnect();
155
	}
186
	}
156
187
(-)Eclipse UI/org/eclipse/ui/IWorkingSet.java (-2 / +5 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2008 IBM Corporation and others.
2
 * Copyright (c) 2000, 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 26-32 Link Here
26
public interface IWorkingSet extends IPersistableElement, IAdaptable {
26
public interface IWorkingSet extends IPersistableElement, IAdaptable {
27
    /**
27
    /**
28
     * Returns the elements that are contained in this working set.
28
     * Returns the elements that are contained in this working set.
29
     * 
29
     * <p>
30
     * This method might throw an {@link IllegalStateException} if 
31
     * the working set is invalid.
32
     * </p>
30
     * @return	the working set's elements
33
     * @return	the working set's elements
31
     */
34
     */
32
    public IAdaptable[] getElements();
35
    public IAdaptable[] getElements();
(-)Eclipse UI Tests/org/eclipse/ui/tests/api/IAggregateWorkingSetTest.java (-103 / +209 lines)
Lines 1-103 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2008 IBM Corporation and others.
2
 * Copyright (c) 2000, 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.ui.tests.api;
11
package org.eclipse.ui.tests.api;
12
12
13
import org.eclipse.core.resources.IWorkspace;
13
import org.eclipse.core.resources.IWorkspace;
14
import org.eclipse.core.resources.ResourcesPlugin;
14
import org.eclipse.core.resources.ResourcesPlugin;
15
import org.eclipse.core.runtime.IAdaptable;
15
import org.eclipse.core.runtime.IAdaptable;
16
import org.eclipse.ui.IAggregateWorkingSet;
16
import org.eclipse.ui.IAggregateWorkingSet;
17
import org.eclipse.ui.IMemento;
17
import org.eclipse.ui.IMemento;
18
import org.eclipse.ui.IWorkingSet;
18
import org.eclipse.ui.IWorkingSet;
19
import org.eclipse.ui.IWorkingSetManager;
19
import org.eclipse.ui.IWorkingSetManager;
20
import org.eclipse.ui.XMLMemento;
20
import org.eclipse.ui.XMLMemento;
21
import org.eclipse.ui.internal.IWorkbenchConstants;
21
import org.eclipse.ui.internal.IWorkbenchConstants;
22
import org.eclipse.ui.tests.harness.util.ArrayUtil;
22
import org.eclipse.ui.tests.harness.util.ArrayUtil;
23
import org.eclipse.ui.tests.harness.util.UITestCase;
23
import org.eclipse.ui.tests.harness.util.UITestCase;
24
24
25
public class IAggregateWorkingSetTest extends UITestCase {
25
public class IAggregateWorkingSetTest extends UITestCase {
26
26
27
	final static String WORKING_SET_NAME = "testws";
27
	final static String WORKING_SET_NAME = "testws";
28
	final static String AGGREGATE_WORKING_SET_NAME_ = "testaggregatews";
28
	final static String AGGREGATE_WORKING_SET_NAME_ = "testaggregatews";
29
29
30
	IWorkspace fWorkspace;
30
	IWorkspace fWorkspace;
31
31
32
	IWorkingSet[] components;
32
	IWorkingSet[] components;
33
	IAggregateWorkingSet fWorkingSet;
33
	IAggregateWorkingSet fWorkingSet;
34
34
35
	public IAggregateWorkingSetTest(String testName) {
35
	public IAggregateWorkingSetTest(String testName) {
36
		super(testName);
36
		super(testName);
37
	}
37
	}
38
38
39
	protected void doSetUp() throws Exception {
39
	protected void doSetUp() throws Exception {
40
		super.doSetUp();
40
		super.doSetUp();
41
		IWorkingSetManager workingSetManager = fWorkbench
41
		IWorkingSetManager workingSetManager = fWorkbench
42
		.getWorkingSetManager();
42
		.getWorkingSetManager();
43
43
44
		fWorkspace = ResourcesPlugin.getWorkspace();
44
		fWorkspace = ResourcesPlugin.getWorkspace();
45
		components = new IWorkingSet[4];
45
		components = new IWorkingSet[4];
46
		for (int i = 0; i < 4; i++) {
46
		for (int i = 0; i < 4; i++) {
47
			components[i] = workingSetManager.createWorkingSet(WORKING_SET_NAME
47
			components[i] = workingSetManager.createWorkingSet(WORKING_SET_NAME
48
					+ i, new IAdaptable[] {});
48
					+ i, new IAdaptable[] {});
49
			workingSetManager.addWorkingSet(components[i]);
49
			workingSetManager.addWorkingSet(components[i]);
50
		}
50
		}
51
		fWorkingSet = (IAggregateWorkingSet) workingSetManager
51
		fWorkingSet = (IAggregateWorkingSet) workingSetManager
52
		.createAggregateWorkingSet(AGGREGATE_WORKING_SET_NAME_,
52
		.createAggregateWorkingSet(AGGREGATE_WORKING_SET_NAME_,
53
				AGGREGATE_WORKING_SET_NAME_, components);
53
				AGGREGATE_WORKING_SET_NAME_, components);
54
54
55
		workingSetManager.addWorkingSet(fWorkingSet);
55
		workingSetManager.addWorkingSet(fWorkingSet);
56
	}
56
	}
57
	protected void doTearDown() throws Exception {
57
	protected void doTearDown() throws Exception {
58
		IWorkingSetManager workingSetManager = fWorkbench.getWorkingSetManager();
58
		IWorkingSetManager workingSetManager = fWorkbench.getWorkingSetManager();
59
		workingSetManager.removeWorkingSet(fWorkingSet);
59
		workingSetManager.removeWorkingSet(fWorkingSet);
60
		for (int i = 0; i < components.length; i++) {
60
		for (int i = 0; i < components.length; i++) {
61
			workingSetManager.removeWorkingSet(components[i]);
61
			workingSetManager.removeWorkingSet(components[i]);
62
		}    	
62
		}    	
63
		super.doTearDown();
63
		super.doTearDown();
64
	}
64
	}
65
65
66
	public void testSaveWSet() throws Throwable {
66
	public void testSaveWSet() throws Throwable {
67
		//<possible client code>
67
		//<possible client code>
68
		IWorkingSetManager workingSetManager = fWorkbench
68
		IWorkingSetManager workingSetManager = fWorkbench
69
		.getWorkingSetManager();
69
		.getWorkingSetManager();
70
		IWorkingSet set=workingSetManager.getWorkingSet(AGGREGATE_WORKING_SET_NAME_);
70
		IWorkingSet set=workingSetManager.getWorkingSet(AGGREGATE_WORKING_SET_NAME_);
71
		if(set.isAggregateWorkingSet()){
71
		if(set.isAggregateWorkingSet()){
72
			IWorkingSet[] sets=((IAggregateWorkingSet)set).getComponents();
72
			IWorkingSet[] sets=((IAggregateWorkingSet)set).getComponents();
73
			if(sets.length>=1){
73
			if(sets.length>=1){
74
				sets[0]=null; //client fails to pay enough attention to specs or unknowingly does this
74
				sets[0]=null; //client fails to pay enough attention to specs or unknowingly does this
75
			}
75
			}
76
		}
76
		}
77
		//</possible client code>    	
77
		//</possible client code>    	
78
		//error makes it look like it comes from workingsets api, with no clue about the actual culprit 
78
		//error makes it look like it comes from workingsets api, with no clue about the actual culprit 
79
		IMemento memento=XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET);
79
		IMemento memento=XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET);
80
		set.saveState(memento);
80
		set.saveState(memento);
81
	}
81
	}
82
	public void testGetElemets() throws Throwable {
82
	public void testGetElemets() throws Throwable {
83
		//<possible client code>
83
		//<possible client code>
84
		IWorkingSetManager workingSetManager = fWorkbench
84
		IWorkingSetManager workingSetManager = fWorkbench
85
		.getWorkingSetManager();
85
		.getWorkingSetManager();
86
		IWorkingSet set=workingSetManager.getWorkingSet(AGGREGATE_WORKING_SET_NAME_);
86
		IWorkingSet set=workingSetManager.getWorkingSet(AGGREGATE_WORKING_SET_NAME_);
87
		if(set.isAggregateWorkingSet()){
87
		if(set.isAggregateWorkingSet()){
88
			IWorkingSet[] sets=((IAggregateWorkingSet)set).getComponents();
88
			IWorkingSet[] sets=((IAggregateWorkingSet)set).getComponents();
89
			if(sets.length>1){
89
			if(sets.length>1){
90
				//code 2 fails to pay enough attention to specs or unknowingly does this
90
				//code 2 fails to pay enough attention to specs or unknowingly does this
91
				sets[0]=workingSetManager.createWorkingSet(WORKING_SET_NAME, new IAdaptable[] { fWorkspace.getRoot() }); 
91
				sets[0]=workingSetManager.createWorkingSet(WORKING_SET_NAME, new IAdaptable[] { fWorkspace.getRoot() }); 
92
				//code 1 part  removes a workingset
92
				//code 1 part  removes a workingset
93
				workingSetManager.removeWorkingSet(sets[1]);
93
				workingSetManager.removeWorkingSet(sets[1]);
94
			}
94
			}
95
		}
95
		}
96
		//</possible client code>    	
96
		//</possible client code>    	
97
		
97
		
98
		//unexpected 
98
		//unexpected 
99
		assertTrue(ArrayUtil.equals(
99
		assertTrue(ArrayUtil.equals(
100
				new IAdaptable[] {},
100
				new IAdaptable[] {},
101
				fWorkingSet.getElements()));
101
				fWorkingSet.getElements()));
102
	}
102
	}
103
}
103
104
	/**
105
	 * Core of the problem: while Eclipse is running, name collisions among working sets
106
	 * don't matter. However, on save and restart names will be used to identify working
107
	 * sets, which could possibly lead to cycles in aggregate working sets.
108
	 * 
109
	 * Bottom line: if there are multiple aggregate working sets with the same name, expect
110
	 * trouble on restart.
111
	 * 
112
	 * To create a cycle we have to be creative:
113
	 * - create an aggregate1 with an ID = "testCycle"
114
	 * - create an aggregate2 with an ID = "testCycle" containing aggregate1
115
	 * - save it into IMemento
116
	 * 
117
	 * Now the IMememnto creates a self reference:
118
	 * 
119
	 * <workingSet name="testCycle" label="testCycle" aggregate="true">
120
	 * 	<workingSet IMemento.internal.id="testCycle" />
121
	 * </workingSet>
122
	 * 
123
	 * All we have to do to emulate stack overflow is to create a working set based on this IMemento.
124
	 * 
125
	 * @throws Throwable
126
	 */
127
	public void testWorkingSetCycle() throws Throwable {
128
		IWorkingSetManager manager = fWorkbench.getWorkingSetManager();
129
		
130
		// create an IMemento with a cycle in it
131
		IAggregateWorkingSet aggregate = (IAggregateWorkingSet) manager
132
		.createAggregateWorkingSet("testCycle","testCycle", new IWorkingSet[0]);
133
		
134
		IAggregateWorkingSet aggregate2 = (IAggregateWorkingSet) manager
135
		.createAggregateWorkingSet("testCycle","testCycle", new IWorkingSet[] {aggregate});
136
		
137
		IMemento memento=XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET);
138
		aggregate2.saveState(memento);
139
		
140
		// load the IMemento
141
		IAggregateWorkingSet aggregateReloaded = null;
142
		try {
143
			aggregateReloaded = (IAggregateWorkingSet) manager.createWorkingSet(memento);
144
			manager.addWorkingSet(aggregateReloaded);
145
			aggregateReloaded.getComponents();
146
		} catch (StackOverflowError e) {
147
			e.printStackTrace();
148
			fail("Stack overflow for self-referenced aggregate working set", e);
149
		} finally {
150
			if (aggregateReloaded != null)
151
				manager.removeWorkingSet(aggregateReloaded);
152
		}
153
	}
154
	
155
	/**
156
	 * Tests cleanup of the cycle from an aggregate working set.
157
	 * @throws Throwable
158
	 */
159
	public void testCycleCleanup() throws Throwable {
160
		IWorkingSetManager manager = fWorkbench.getWorkingSetManager();
161
		
162
		// create an IMemento with a cycle in it: { good, good, cycle, good, good }
163
		IAggregateWorkingSet aggregateSub0 = (IAggregateWorkingSet) manager
164
		.createAggregateWorkingSet("testCycle0","testCycle0", new IWorkingSet[0]);
165
		
166
		IAggregateWorkingSet aggregateSub1 = (IAggregateWorkingSet) manager
167
		.createAggregateWorkingSet("testCycle1","testCycle1", new IWorkingSet[0]);
168
169
		IAggregateWorkingSet aggregateSub2 = (IAggregateWorkingSet) manager
170
		.createAggregateWorkingSet("testCycle","testCycle", new IWorkingSet[0]); // cycle
171
		
172
		IAggregateWorkingSet aggregateSub3 = (IAggregateWorkingSet) manager
173
		.createAggregateWorkingSet("testCycle3","testCycle3", new IWorkingSet[0]);
174
		
175
		IAggregateWorkingSet aggregateSub4 = (IAggregateWorkingSet) manager
176
		.createAggregateWorkingSet("testCycle4","testCycle4", new IWorkingSet[0]);
177
		
178
		
179
		IAggregateWorkingSet aggregate = (IAggregateWorkingSet) manager
180
		.createAggregateWorkingSet("testCycle","testCycle", new IWorkingSet[] {aggregateSub0, 
181
				aggregateSub1, aggregateSub2, aggregateSub3, aggregateSub4});
182
183
		manager.addWorkingSet(aggregateSub0);
184
		manager.addWorkingSet(aggregateSub1);
185
		manager.addWorkingSet(aggregateSub3);
186
		manager.addWorkingSet(aggregateSub4);
187
		
188
		IMemento memento=XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKING_SET);
189
		aggregate.saveState(memento);
190
		
191
		// load the IMemento
192
		IAggregateWorkingSet aggregateReloaded = null;
193
		try {
194
			aggregateReloaded = (IAggregateWorkingSet) manager.createWorkingSet(memento);
195
			manager.addWorkingSet(aggregateReloaded);
196
			IWorkingSet[] aggregates = aggregateReloaded.getComponents();
197
			assertNotNull(aggregates);
198
			assertEquals(4, aggregates.length);
199
			for(int i = 0; i < aggregates.length; i++)
200
				assertFalse("testCycle".equals(aggregates[i].getName()));
201
		} catch (StackOverflowError e) {
202
			e.printStackTrace();
203
			fail("Stack overflow for self-referenced aggregate working set", e);
204
		} finally {
205
			if (aggregateReloaded != null)
206
				manager.removeWorkingSet(aggregateReloaded);
207
		}
208
	}
209
}

Return to bug 194917