/******************************************************************************* * 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); } }