1
/*******************************************************************************
2
* Copyright (c) 2007, 2008 Intel Corporation and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Intel Corporation - Initial API and implementation
10
* Anton Leherbauer (Wind River Systems)
11
*******************************************************************************/
12
package org.eclipse.cdt.core.settings.model.util;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.Iterator;
17
import java.util.List;
20
import org.eclipse.core.runtime.IPath;
21
import org.eclipse.core.runtime.Path;
23
public final class PathSettingsContainer {
24
private static final Object INEXISTENT_VALUE = new Object();
25
private static final String ROOY_PATH_NAME = Path.ROOT.toString();
26
// private static final boolean DEBUG = true;
27
// private static final int INIT_CHILDREN_MAP_CAPACITY = 2;
29
// private Map fChildrenMap;
30
// private Map fPatternMap;
31
private PatternNameMap fPatternChildrenMap;
32
private Object fValue;
35
private PathSettingsContainer fRootContainer;
36
private PathSettingsContainer fDirectParentContainer;
37
private List fListeners;
39
private boolean fIsPatternMode;
41
private static final int ADDED = 1;
42
private static final int REMOVED = 2;
43
private static final int VALUE_CHANGED = 3;
44
private static final int PATH_CHANGED = 4;
46
private static class PatternSearchInfo {
48
int fNumDoubleStarEls;
51
public static PathSettingsContainer createRootContainer(){
52
return createRootContainer(false);
55
public static PathSettingsContainer createRootContainer(boolean patternMode){
56
return new PathSettingsContainer(patternMode);
59
private PathSettingsContainer(boolean pattternMode){
60
this(null, null, ROOY_PATH_NAME, pattternMode);
63
private PathSettingsContainer(PathSettingsContainer root, PathSettingsContainer parent, String name, boolean patternMode){
64
fRootContainer = root;
65
fDirectParentContainer = parent;
67
fIsPatternMode = patternMode;
68
if(fRootContainer == null){
69
fRootContainer = this;
70
// fPath = new Path(name);
72
fValue = INEXISTENT_VALUE;
76
private PatternNameMap getPatternChildrenMap(boolean create){
77
if(fPatternChildrenMap == null && create)
78
fPatternChildrenMap = new PatternNameMap();
79
return fPatternChildrenMap;
82
private PathSettingsContainer getExacChild(String name, boolean create){
83
PatternNameMap pMap = getPatternChildrenMap(create);
85
PathSettingsContainer child = (PathSettingsContainer)pMap.get(name);
86
if(child == null && create){
87
child = new PathSettingsContainer(fRootContainer, this, name, fIsPatternMode);
95
private boolean isDoubleStarName(){
96
return PatternNameMap.DOUBLE_STAR_PATTERN.equals(getName());
99
private PathSettingsContainer getDoubleStarChild(){
100
PatternNameMap pMap = getPatternChildrenMap(false);
102
return pMap.containsDoubleStar() ? (PathSettingsContainer)pMap.get(PatternNameMap.DOUBLE_STAR_PATTERN) : null;
107
private List getChildren(String name){
108
PatternNameMap pMap = getPatternChildrenMap(false);
110
return pMap.getValues(name);
116
private void notifyChange(PathSettingsContainer container, int type, Object oldValue, boolean childrenAffected){
117
List list = getListenersList(false);
118
if(list != null && list.size() > 0){
119
for(Iterator iter = list.iterator(); iter.hasNext();){
122
((IPathSettingsContainerListener)iter.next()).containerAdded(container);
125
((IPathSettingsContainerListener)iter.next()).aboutToRemove(container);
128
((IPathSettingsContainerListener)iter.next()).containerValueChanged(container, oldValue);
131
((IPathSettingsContainerListener)iter.next()).containerPathChanged(container, (IPath)oldValue, childrenAffected);
137
PathSettingsContainer parent = getParentContainer();
139
parent.notifyChange(container, type, oldValue, childrenAffected);
142
private List getListenersList(boolean create){
143
if(fListeners == null && create)
144
fListeners = new ArrayList();
148
public boolean hasChildren(){
149
PatternNameMap pMap = getPatternChildrenMap(false);
150
return pMap != null && pMap.size() != 0;
153
public PathSettingsContainer getChildContainer(IPath path, boolean create, boolean exactPath){
154
return getChildContainer(path, create, exactPath, fIsPatternMode);
157
public PathSettingsContainer getChildContainer(IPath path, boolean create, boolean exactPath, boolean patternSearch){
158
PathSettingsContainer container = findContainer(path, create, exactPath, patternSearch, -1, null);
159
if(container != null && container.internalGetValue() == INEXISTENT_VALUE){
161
container.internalSetValue(null);
162
notifyChange(container, ADDED, null, false);
163
} else if(!exactPath){
165
container.internalGetValue() == INEXISTENT_VALUE;
166
container = container.getDirectParentContainer());
167
} else if(container.internalGetValue() == INEXISTENT_VALUE){
175
static IPath toNormalizedContainerPath(IPath path){
176
return Path.ROOT.append(path);
179
public PathSettingsContainer[] getChildren(final boolean includeThis){
180
final List list = new ArrayList();
181
accept(new IPathSettingsContainerVisitor(){
183
public boolean visit(PathSettingsContainer container) {
184
if(container != PathSettingsContainer.this || includeThis)
190
return (PathSettingsContainer[])list.toArray(new PathSettingsContainer[list.size()]);
193
public PathSettingsContainer[] getChildrenForPath(IPath path, boolean includePath){
194
PathSettingsContainer cr = findContainer(path, false, true, fIsPatternMode, -1, null);
196
return cr.getChildren(includePath);
197
return new PathSettingsContainer[0];
200
public PathSettingsContainer[] getDirectChildrenForPath(IPath path){
201
PathSettingsContainer cr = findContainer(path, false, true, fIsPatternMode, -1, null);
203
return cr.getDirectChildren();
204
return new PathSettingsContainer[0];
207
/* public PathSettingsContainer[] getDirectChildrenForPath(IPath path, boolean searchPatterns){
209
return getDirectChildrenForPath(path);
211
if(!isRoot() && !PatternNameMap.isPatternName(fName)){
212
return getDirectParentContainer().getDirectChildrenForPath(
213
new Path(fName).append(path), true);
215
return searchPatternsDirectChildrenForPath(path);
218
private PathSettingsContainer[] searchPatternsDirectChildrenForPath(IPath path){
219
Set set = new HashSet();
220
findContainer(path, false, false, path.segmentCount(), set);
223
for(Iterator iter = set.iterator(); iter.hasNext();){
224
PathSettingsContainer child = (PathSettingsContainer)iter.next();
225
if(child.fValue == INEXISTENT_VALUE)
226
throw new IllegalStateException();
230
return (PathSettingsContainer[])set.toArray(new PathSettingsContainer[set.size()]);
233
public PathSettingsContainer[] getDirectChildren(){
234
List list = doGetDirectChildren(null);
235
if(list == null || list.size() == 0)
236
return new PathSettingsContainer[0];
237
return (PathSettingsContainer[])list.toArray(new PathSettingsContainer[list.size()]);
240
private List doGetDirectChildren(List list){
241
PatternNameMap pMap = getPatternChildrenMap(false);
244
list = new ArrayList();
245
for(Iterator iter = pMap.values().iterator(); iter.hasNext(); ){
246
PathSettingsContainer cr = (PathSettingsContainer)iter.next();
247
if(cr.fValue == INEXISTENT_VALUE){
248
cr.doGetDirectChildren(list);
257
public Object[] getValues(final boolean includeThis){
258
final List list = new ArrayList();
259
accept(new IPathSettingsContainerVisitor(){
261
public boolean visit(PathSettingsContainer container) {
262
if(container != PathSettingsContainer.this || includeThis)
263
list.add(container.getValue());
268
return list.toArray();
271
public PathSettingsContainer getParentContainer(){
272
if(fDirectParentContainer != null)
273
return fDirectParentContainer.getValidContainer();
277
private PathSettingsContainer getValidContainer(){
278
if(internalGetValue() == INEXISTENT_VALUE)
279
return getDirectParentContainer().getValidContainer();
283
public Object removeChildContainer(IPath path){
284
PathSettingsContainer container = getChildContainer(path, false, true);
286
if(container != null){
287
value = container.getValue();
293
public void remove(){
297
if(fValue != INEXISTENT_VALUE){
298
notifyChange(this, REMOVED, null, false);
299
internalSetValue(INEXISTENT_VALUE);
302
if (fDirectParentContainer != null) {
303
fDirectParentContainer.deleteChild(this);
304
fDirectParentContainer.checkRemove();
305
fDirectParentContainer = null;
307
fRootContainer = null;
311
private void checkRemove(){
312
if(fValue == INEXISTENT_VALUE && !hasChildren())
316
private void disconnectChild(PathSettingsContainer child){
317
getPatternChildrenMap(true).remove(child.getName());
320
private void connectChild(PathSettingsContainer child){
321
getPatternChildrenMap(true).put(child.getName(), child);
324
public boolean isValid(){
325
return fValue != INEXISTENT_VALUE && fRootContainer != null;
328
public void removeChildren(){
329
PatternNameMap pMap = getPatternChildrenMap(false);
330
if(pMap == null || pMap.size() == 0)
334
Collection c = pMap.values();
335
PathSettingsContainer childContainers[] = (PathSettingsContainer[])c.toArray(new PathSettingsContainer[c.size()]);
337
for(int i = 0; i < childContainers.length; i++){
338
childContainers[i].removeChildren();
339
childContainers[i].remove();
343
private void deleteChild(PathSettingsContainer child){
344
getPatternChildrenMap(false).remove(child.getName());
347
private String getName(){
351
private PathSettingsContainer findContainer(IPath path, boolean create, boolean exactPath, boolean patternSearch, int matchDepth, PatternSearchInfo psi){
352
PathSettingsContainer container = null;
353
if(path.segmentCount() == 0)
355
else if (create || exactPath || !patternSearch || !fIsPatternMode) {
356
PathSettingsContainer child = getExacChild(path.segment(0), create);
358
container = child.findContainer(path.removeFirstSegments(1), create, exactPath, patternSearch, stepDepth(matchDepth), psi);
362
//looking for container using patterns in read mode (i.e. not creating new container)
364
psi = new PatternSearchInfo();
365
container = processPatterns(path, matchDepth, 0, psi);
367
// List list = getChildren(path.segment(0));
369
// int size = list.size();
370
// PathSettingsContainer child, childFound;
372
// for(int i = 0; i < size; i++){
373
// child = (PathSettingsContainer)list.get(i);
374
// if(directChildren && child.fValue != INEXISTENT_VALUE){
375
// childFound = child;
377
// childFound = child.findContainer(path.removeFirstSegments(1), false, false, directChildren, storeSet);
380
// if(childFound.fValue != INEXISTENT_VALUE){
381
// if(container == null
382
// || container.getValue() == INEXISTENT_VALUE
383
// || container.getPath().segmentCount() < childFound.getPath().segmentCount()){
384
// container = childFound;
386
// if(storeSet != null)
387
// storeSet.add(container);
391
// if(container == null || storeSet != null){
392
// PathSettingsContainer dsChild = getDoubleStarChild();
393
// if(dsChild != null && !storeSet.contains(dsChild)){
394
// container = dsChild.findContainer(path, false, false, directChildren, storeSet);
397
// if(container == null){
398
// if(isDoubleStarName()){
399
// if(path.segmentCount() != 0){
400
// path = path.removeFirstSegments(1);
414
static boolean pathsEqual(IPath p1, IPath p2) {
418
int i = p1.segmentCount();
419
if(i != p2.segmentCount())
423
if (!p1.segment(i).equals(p2.segment(i)))
429
private PathSettingsContainer processPatterns(IPath path, int matchDepth, int depth, PatternSearchInfo psi){
430
Set storeSet = psi.fStoreSet;
431
PathSettingsContainer container = null;
432
String name = path.segment(0);
433
List list = getChildren(name);
434
PathSettingsContainer child, childFound;
435
boolean exactPathFound = false;
437
int size = list.size();
439
for(int i = 0; i < size; i++){
440
child = (PathSettingsContainer)list.get(i);
441
if(matchDepth == 0 && child.fValue != INEXISTENT_VALUE){
444
childFound = child.findContainer(path.removeFirstSegments(1), false, false, true, stepDepth(matchDepth), psi);
447
if(childFound != null && childFound.fValue != INEXISTENT_VALUE){
449
&& path.segmentCount() == 1
450
&& child != childFound
451
&& name.equals(childFound.fName)){
452
container = childFound;
453
exactPathFound = true;
454
} else if(container == null
455
|| container.getValue() == INEXISTENT_VALUE
456
|| container.getPath().segmentCount() < childFound.getPath().segmentCount()){
457
container = childFound;
460
storeSet.add(container);
461
else if(exactPathFound)
467
if(!exactPathFound || storeSet != null){
468
child = getDoubleStarChild();
469
if(child != null/* && !storeSet.contains(child)*/){
470
if(matchDepth == 0 && child.fValue != INEXISTENT_VALUE){
473
childFound = child.findContainer(path, false, false, true, matchDepth, psi);
476
if(childFound != null && childFound.fValue != INEXISTENT_VALUE){
477
psi.fNumDoubleStarEls++;
479
|| container.getValue() == INEXISTENT_VALUE
480
|| container.getPath().segmentCount() < childFound.getPath().segmentCount() + depth - psi.fNumDoubleStarEls){
481
container = childFound;
484
storeSet.add(container);
488
if(container == null){
489
if(isDoubleStarName()){
490
if(path.segmentCount() > 1){
491
childFound = processPatterns(path.removeFirstSegments(1), stepDepth(matchDepth), depth + 1, psi);
492
if(childFound != null && childFound.fValue != INEXISTENT_VALUE){
493
container = childFound;
495
storeSet.add(container);
498
} else if (matchDepth < 0 && fValue != INEXISTENT_VALUE){
501
storeSet.add(container);
508
private int stepDepth(int depth){
509
return depth == 0 ? depth : depth-1;
512
public void accept(IPathSettingsContainerVisitor visitor){
516
private boolean doAccept(IPathSettingsContainerVisitor visitor){
517
if(fValue != INEXISTENT_VALUE && !visitor.visit(this))
520
PatternNameMap pMap = getPatternChildrenMap(false);
522
for(Iterator iter = pMap.values().iterator(); iter.hasNext();){
523
PathSettingsContainer child = (PathSettingsContainer)iter.next();
524
if(!child.doAccept(visitor))
532
public IPath getPath(){
534
if(fDirectParentContainer != null)
535
fPath = fDirectParentContainer.getPath().append(fName);
542
public void setPath(IPath path, boolean moveChildren){
543
if(path == null || isRoot() || path.equals(getPath()) || path.segmentCount() == 0)
546
IPath oldPath = getPath();
548
fDirectParentContainer.disconnectChild(this);
552
PathSettingsContainer cr = new PathSettingsContainer(fRootContainer, fDirectParentContainer, fName, fIsPatternMode);
553
for(Iterator iter = fPatternChildrenMap.values().iterator(); iter.hasNext();){
554
PathSettingsContainer child = (PathSettingsContainer)iter.next();
557
cr.connectChild(child);
564
PathSettingsContainer newParent = fRootContainer.findContainer(path.removeLastSegments(1), true, true, false, -1, null);
565
PathSettingsContainer oldParent = fDirectParentContainer;
566
fName = path.segment(path.segmentCount()-1);
569
setParent(newParent);
570
newParent.connectChild(this);
572
oldParent.checkRemove();
573
notifyChange(this, PATH_CHANGED, oldPath, moveChildren);
576
private Object internalGetValue(){
580
public boolean isRoot(){
581
return fRootContainer == this;
584
private Object internalSetValue(Object value){
585
Object oldValue = fValue;
587
if(oldValue == INEXISTENT_VALUE){
589
} else if(fValue != INEXISTENT_VALUE){
590
notifyChange(this, VALUE_CHANGED, oldValue, false);
595
public Object setValue(Object value){
596
if(fValue == INEXISTENT_VALUE)
597
throw new IllegalStateException();
598
return internalSetValue(value);
601
public Object getValue(){
602
if(fValue == INEXISTENT_VALUE)
603
throw new IllegalStateException();
607
public PathSettingsContainer getRootContainer(){
608
return fRootContainer;
611
private PathSettingsContainer getDirectParentContainer(){
612
return fDirectParentContainer;
615
public void addContainerListener(IPathSettingsContainerListener listenet){
616
List list = getListenersList(true);
620
public void removeContainerListener(IPathSettingsContainerListener listenet){
621
List list = getListenersList(false);
623
list.remove(listenet);
626
private void setParent(PathSettingsContainer parent){
627
fDirectParentContainer = parent;
631
public String toString() {
632
return contributeToString(new StringBuffer(), 0).toString();
635
private StringBuffer contributeToString(StringBuffer buf, int depth){
636
for (int i= 0; i < depth; i++) {
639
buf.append('[').append(getPath()).append(']').append('\n');
641
PathSettingsContainer[] directChildren = getDirectChildren();
642
if(directChildren.length != 0){
643
int nextDepth = depth + 1;
644
for(int i = 0; i < directChildren.length; i++){
645
directChildren[i].contributeToString(buf, nextDepth);
651
static boolean hasSpecChars(IPath path){
652
int count = path.segmentCount();
653
for(int i = 0; i < count; i++){
654
if(PatternNameMap.isPatternName(path.segment(i)))