blob: 5d8670e6d1901b96f437d12a00542c23ac88beb8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.wst.jsdt.web.core.internal;
import java.io.File;
import org.eclipse.core.runtime.IPath;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
/**
* <p>Utility class dealing with {@link IPath}s.</p>
*/
public class PathUtils {
/** represents the * pattern in a path pattern */
private static final String STAR = "*"; //$NON-NLS-1$
/** represents the ** pattern in a path pattern */
private static final String STAR_STAR = "**"; //$NON-NLS-1$
/**
* <p>Counts the number of segments in a given pattern that match segments in a given parent path.
* This counting takes place from the beginning of both the pattern and parent and stops when
* they no longer match. The pattern can contain **, * and ? wild cards.</p>
*
* @param pattern count the number of segments of this pattern that match the given <code>parent</code>
* @param parent count the number of segments in the given <code>pattern</code> that match this path
* @return the number of segments from the beginning of the given <code>pattern</code> {@link IPath}
* that match the beginning segments of the given <code>parent</code> {@link IPath}
*/
public static int countPatternSegmentsThatMatchParent(IPath pattern, IPath parent) {
int matchingSegments = 0;
//ignore a pattern that is just ** or *
if(!(pattern.segmentCount() == 1 &&
(pattern.segment(0).equals(STAR_STAR) || pattern.segment(0).equals(STAR)))) {
int patternSegmentIndex = 0;
int parentSegmentIndex = 0;
boolean starStarMode = false;
while(patternSegmentIndex < pattern.segmentCount() &&
parentSegmentIndex < parent.segmentCount()) {
String patternSegment = pattern.segment(patternSegmentIndex);
String parentSegment = parent.segment(parentSegmentIndex);
/* if matching on wild
* else if wild match on multiple path segments
* else if wild match on one path segment or path segments are equal
* else not equal so stop comparing
*/
if(starStarMode) {
/* if parent segment equals first pattern segment after a ** stop matching on it
* else still matching on **
*/
if(pathSegmentMatchesPattern(patternSegment, parentSegment)) {
starStarMode = false;
matchingSegments++;
patternSegmentIndex++;
parentSegmentIndex++;
} else {
parentSegmentIndex++;
}
} else if(patternSegment.equals(STAR_STAR)) { //$NON-NLS-1$
starStarMode = true;
//find the first pattern segment after the ** that is not another ** or *
matchingSegments++;
parentSegmentIndex++;
for(int i = patternSegmentIndex+1; i < pattern.segmentCount(); ++i) {
if(!(pattern.segment(i).equals(STAR_STAR) || //$NON-NLS-1$
pattern.segment(i).equals(STAR))) { //$NON-NLS-1$
patternSegmentIndex = i;
break;
}
}
} else if(patternSegment.equals("*") || //$NON-NLS-1$
pathSegmentMatchesPattern(patternSegment, parentSegment)){
matchingSegments++;
patternSegmentIndex++;
parentSegmentIndex++;
} else {
break;
}
}
}
return matchingSegments;
}
/**
* <p>Given a pattern path and a parent path attempts to truncate the given pattern path such
* that it is relative to the given parent path.</p>
*
* @param pattern attempt to truncate this {@link IPath} such that it is relative to the given
* <code>parent</code> {@link IPath}
* @param parent attempt to truncate the given <code>pattern</code> {@link IPath} such that it
* is relative to this {@link IPath}
* @return either a truncated version of the given <code>pattern</code> {@link IPath} that is
* relative to the given <code>parent</code> {@link IPath}, or <code>null</code> if the given
* <code>pattern</code> {@link IPath} could not be truncated to be relative to the given
* <code>parent</code> {@link IPath}
*/
public static IPath makePatternRelativeToParent(IPath pattern, IPath parent) {
int matchedSegments = countPatternSegmentsThatMatchParent(pattern, parent);
IPath relativePattern = null;
if(matchedSegments != 0) {
relativePattern = pattern.removeFirstSegments(matchedSegments);
if(relativePattern.segmentCount() == 0) {
relativePattern = null;
} else {
relativePattern.makeRelative();
}
}
return relativePattern;
}
/**
* <p>A convenience method for checking the matching of one segment from a pattern path with
* one segment from a path.</p>
*Bug 334922 - CharOperation#match does not work as expected when isCaseSensitive is passed as false
* @param patternSegment check if this pattern segment is a match with the given path segment.
* @param segment check if this path segment matches with the given pattern segment
* @return <code>true</code> if the segments match, <code>false</code> otherwise
*/
private static boolean pathSegmentMatchesPattern(String patternSegment, String segment) {
return CharOperation.pathMatch(patternSegment.toCharArray(), segment.toCharArray(),
false, File.separatorChar);
}
}