From c2919ed4f940f0e23aa90739bc9b7de13063ab24 Mon Sep 17 00:00:00 2001 From: Alena Laskavaia Date: Mon, 24 May 2010 15:05:39 +0000 Subject: [PATCH] Added scope preference for checkers --- .../cxx/model/AbstractIndexAstChecker.java | 3 +- .../cdt/codan/core/model/AbstractChecker.java | 11 +- ...AbstractCheckerWithProblemPreferences.java | 63 +- .../core/param/AbstractProblemPreference.java | 36 +- .../core/param/BasicProblemPreference.java | 21 +- .../param/FileScopeProblemPreference.java | 264 ++ .../param/IProblemPreferenceDescriptor.java | 6 +- .../core/param/ListProblemPreference.java | 10 +- .../core/param/MapProblemPreference.java | 9 +- .../codan/internal/core/CharOperation.java | 2703 +++++++++++++++++ .../internal/core/model/CodanProblem.java | 6 +- .../codan/internal/ui/CodanUIMessages.java | 34 + .../ui/dialogs/CustomizeProblemDialog.java | 32 +- .../ExclusionInclusionEntryDialog.java | 274 ++ .../cdt/codan/internal/ui/messages.properties | 50 + .../ui/preferences/CodanPreferencePage.java | 2 +- .../preferences/FileScopePreferencePage.java | 302 ++ .../ui/widgets/BasicElementLabels.java | 147 + .../CustomizeProblemComposite.java | 22 +- .../ui/widgets/FileScopeComposite.java | 100 + .../ParametersComposite.java | 33 +- 21 files changed, 4069 insertions(+), 59 deletions(-) create mode 100644 codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/FileScopeProblemPreference.java create mode 100644 codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CharOperation.java create mode 100644 codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ExclusionInclusionEntryDialog.java create mode 100644 codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/FileScopePreferencePage.java create mode 100644 codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/BasicElementLabels.java rename codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/{dialogs => widgets}/CustomizeProblemComposite.java (79%) create mode 100644 codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/FileScopeComposite.java rename codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/{dialogs => widgets}/ParametersComposite.java (87%) diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/model/AbstractIndexAstChecker.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/model/AbstractIndexAstChecker.java index bc2491583db..152887e0a8d 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/model/AbstractIndexAstChecker.java +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/model/AbstractIndexAstChecker.java @@ -63,6 +63,7 @@ public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblem } public synchronized boolean processResource(IResource resource) { + if (!shouldProduceProblems(resource)) return false; if (resource instanceof IFile) { IFile file = (IFile) resource; try { @@ -102,7 +103,7 @@ public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblem else loc = getRuntime().getProblemLocationFactory() .createProblemLocation(astFile, line); - getProblemReporter().reportProblem(id, loc, args); + reportProblem(id, loc, args); } @Override diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java index 469bab1f7c7..ccdfe9297a6 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java @@ -65,9 +65,9 @@ public abstract class AbstractChecker implements IChecker { * - file in scope * @return problem instance */ - public IProblem getProblemById(String id, IFile file) { - IProblem problem = CheckersRegistry.getInstance().getResourceProfile( - file).findProblem(id); + public IProblem getProblemById(String id, IResource file) { + IProblem problem = CheckersRegistry.getInstance() + .getResourceProfile(file).findProblem(id); if (problem == null) throw new IllegalArgumentException("Id is not registered"); //$NON-NLS-1$ return problem; @@ -144,4 +144,9 @@ public abstract class AbstractChecker implements IChecker { public boolean runInEditor() { return this instanceof IRunnableInEditorChecker; } + + public void reportProblem(String problemId, IProblemLocation loc, + Object... args) { + getProblemReporter().reportProblem(problemId, loc, args); + } } diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractCheckerWithProblemPreferences.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractCheckerWithProblemPreferences.java index da6836e0d0d..1600da6007b 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractCheckerWithProblemPreferences.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractCheckerWithProblemPreferences.java @@ -10,11 +10,17 @@ *******************************************************************************/ package org.eclipse.cdt.codan.core.model; +import java.util.Collection; +import java.util.Iterator; + import org.eclipse.cdt.codan.core.param.BasicProblemPreference; +import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; import org.eclipse.cdt.codan.core.param.IProblemPreference; import org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor.PreferenceType; import org.eclipse.cdt.codan.core.param.ListProblemPreference; import org.eclipse.cdt.codan.core.param.MapProblemPreference; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; /** * AbstarctChecker that has extra methods to simplify adding problem @@ -28,7 +34,55 @@ public abstract class AbstractCheckerWithProblemPreferences extends * Checker that actually has parameter must override this */ public void initPreferences(IProblemWorkingCopy problem) { - // do nothing + // by default add file scope preference + addPreference(problem, new FileScopeProblemPreference(), null); + } + + /** + * @param problem + * @return + */ + public FileScopeProblemPreference getScopePreference(IProblem problem) { + FileScopeProblemPreference scope = (FileScopeProblemPreference) getTopLevelPreferenceMap( + problem).getChildDescriptor(FileScopeProblemPreference.KEY); + return scope; + } + + /** + * User can scope out some resources for this checker. Checker can use this + * call to test if it should run on this resource at all or not. Test should + * be done within processResource method not in enabledInContext. + * + * @param res + * @return + */ + public boolean shouldProduceProblems(IResource res) { + Collection refProblems = getRuntime().getChechersRegistry() + .getRefProblems(this); + for (Iterator iterator = refProblems.iterator(); iterator + .hasNext();) { + IProblem checkerProblem = iterator.next(); + if (shouldProduceProblem( + getProblemById(checkerProblem.getId(), res), + res.getLocation())) + return true; + } + return false; + } + + public boolean shouldProduceProblem(IProblem problem, IPath resource) { + FileScopeProblemPreference scope = getScopePreference(problem); + if (scope == null) + return true; + return scope.isInScope(resource); + } + + @Override + public void reportProblem(String problemId, IProblemLocation loc, + Object... args) { + if (shouldProduceProblem(getProblemById(problemId, loc.getFile()), loc + .getFile().getLocation())) + super.reportProblem(problemId, loc, args); } /** @@ -102,13 +156,14 @@ public abstract class AbstractCheckerWithProblemPreferences extends * @param problem * @return */ - protected MapProblemPreference getTopLevelPreferenceMap( - IProblemWorkingCopy problem) { + protected MapProblemPreference getTopLevelPreferenceMap(IProblem problem) { MapProblemPreference map = (MapProblemPreference) problem .getPreference(); if (map == null) { map = new MapProblemPreference("params", ""); //$NON-NLS-1$ //$NON-NLS-2$ - problem.setPreference(map); + if (problem instanceof IProblemWorkingCopy) { + ((IProblemWorkingCopy) problem).setPreference(map); + } } return map; } diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/AbstractProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/AbstractProblemPreference.java index a6a1e7ac9df..a5b6520b020 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/AbstractProblemPreference.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/AbstractProblemPreference.java @@ -11,6 +11,7 @@ package org.eclipse.cdt.codan.core.param; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; @@ -150,6 +151,39 @@ public abstract class AbstractProblemPreference implements IProblemPreference { /** * @param tokenizer * @return + * @throws IOException */ - public abstract void importValue(StreamTokenizer tokenizer); + public abstract void importValue(StreamTokenizer tokenizer) + throws IOException; + + public void importValue(String str) { + StreamTokenizer tokenizer = getImportTokenizer(str); + try { + importValue(tokenizer); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(str, e); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + protected String escape(String x) { + x = x.replaceAll("[\"\\\\]", "\\\\$0"); //$NON-NLS-1$//$NON-NLS-2$ + return "\"" + x + "\""; //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * @param str + * @return + */ + protected String unescape(String str) { + StreamTokenizer tokenizer = getImportTokenizer(str); + try { + tokenizer.nextToken(); + } catch (IOException e) { + return null; + } + String sval = tokenizer.sval; + return sval; + } } diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/BasicProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/BasicProblemPreference.java index f1e40f10e29..a6a0a7f14f8 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/BasicProblemPreference.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/BasicProblemPreference.java @@ -81,11 +81,6 @@ public class BasicProblemPreference extends AbstractProblemPreference { return x; } - protected String escape(String x) { - x = x.replaceAll("[\"\\\\]", "\\\\$0"); //$NON-NLS-1$//$NON-NLS-2$ - return "\"" + x + "\""; //$NON-NLS-1$//$NON-NLS-2$ - } - /* * (non-Javadoc) * @@ -93,6 +88,7 @@ public class BasicProblemPreference extends AbstractProblemPreference { * org.eclipse.cdt.codan.core.param.IProblemPreferenceValue#importValue( * java.lang.String) */ + @Override public void importValue(String str) { if (str.startsWith("\"")) //$NON-NLS-1$ str = unescape(str); @@ -115,21 +111,6 @@ public class BasicProblemPreference extends AbstractProblemPreference { } } - /** - * @param str - * @return - */ - protected String unescape(String str) { - StreamTokenizer tokenizer = getImportTokenizer(str); - try { - tokenizer.nextToken(); - } catch (IOException e) { - return null; - } - String sval = tokenizer.sval; - return sval; - } - @Override public String toString() { return "(" + type + ")" + key + ((value == null) ? "" : "=" + value); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/FileScopeProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/FileScopeProblemPreference.java new file mode 100644 index 00000000000..a75ebee4af9 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/FileScopeProblemPreference.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2009,2010 QNX Software Systems + * 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: + * QNX Software Systems (Alena Laskavaia) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.param; + +import java.io.IOException; +import java.io.StreamTokenizer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.codan.internal.core.CharOperation; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * TODO: add description + */ +public class FileScopeProblemPreference extends AbstractProblemPreference { + public static final String KEY = "fileScope"; //$NON-NLS-1$ + public static final String EXCLUSION = "exclusion"; //$NON-NLS-1$ + public static final String INCLUSION = "inclusion"; //$NON-NLS-1$ + private IResource resource; + private IPath[] inclusion = new IPath[0]; + private IPath[] exclusion = new IPath[0]; + + public FileScopeProblemPreference() { + setKey(KEY); + setLabel("File Exclusion and Inclusion"); + setType(PreferenceType.TYPE_CUSTOM); + } + + /** + * @param key + * @return + */ + public IPath[] getAttribute(String key) { + if (key == EXCLUSION) + return exclusion; + if (key == INCLUSION) + return inclusion; + return null; + } + + public void setAttribute(String key, IPath[] value) { + if (key == EXCLUSION) + exclusion = value.clone(); + if (key == INCLUSION) + inclusion = value.clone(); + } + + /** + * @return + */ + public IProject getProject() { + if (resource != null) + return resource.getProject(); + return null; + } + + /** + * @return + */ + public IPath getPath() { + if (resource != null) + return resource.getFullPath(); + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + return root.getFullPath(); + } + + /** + * @param resource + * the resource to set + */ + public void setResource(IResource resource) { + this.resource = resource; + } + + /** + * @return the resource + */ + public IResource getResource() { + return resource; + } + + public String exportValue() { + return exportPathList(INCLUSION, inclusion) + "," //$NON-NLS-1$ + + exportPathList(EXCLUSION, exclusion); + } + + /** + * @param inclusion2 + * @param inclusion3 + * @return + */ + private String exportPathList(String key, IPath[] arr) { + String res = key + "=>("; //$NON-NLS-1$ + for (int i = 0; i < arr.length; i++) { + if (i != 0) + res += ","; //$NON-NLS-1$ + res += escape(arr[i].toPortableString()); + } + return res + ")"; //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.cdt.codan.core.param.AbstractProblemPreference#importValue + * (java.io.StreamTokenizer) + */ + @Override + public void importValue(StreamTokenizer tokenizer) throws IOException { + List inc = importPathList(tokenizer, INCLUSION); + inclusion = inc.toArray(new IPath[inc.size()]); + checkChar(tokenizer, ','); + List exc = importPathList(tokenizer, EXCLUSION); + exclusion = exc.toArray(new IPath[exc.size()]); + } + + /** + * @param tokenizer + * @param c + * @throws IOException + */ + private void checkChar(StreamTokenizer tokenizer, char c) + throws IOException { + tokenizer.nextToken(); + if (tokenizer.ttype != c) + throw new IllegalArgumentException("Expected " + c); //$NON-NLS-1$ + } + + /** + * @param tokenizer + * @param inclusion2 + * @throws IOException + * @throws IllegalAccessException + */ + private void checkKeyword(StreamTokenizer tokenizer, String keyword) + throws IOException { + tokenizer.nextToken(); + if (tokenizer.sval == null || !tokenizer.sval.equals(keyword)) + throw new IllegalArgumentException("Expected " + keyword); //$NON-NLS-1$ + } + + protected List importPathList(StreamTokenizer tokenizer, + String keyword) throws IOException { + checkKeyword(tokenizer, keyword); + checkChar(tokenizer, '='); + checkChar(tokenizer, '>'); + ArrayList list = new ArrayList(); + int token; + int index = 0; + try { + checkChar(tokenizer, '('); + token = tokenizer.nextToken(); + if (token != ')') + tokenizer.pushBack(); + else + return Collections.emptyList(); + while (true) { + token = tokenizer.nextToken(); + if (tokenizer.sval == null) + throw new IllegalArgumentException(); + list.add(new Path(tokenizer.sval)); + token = tokenizer.nextToken(); + if (token == ')') + break; + tokenizer.pushBack(); + checkChar(tokenizer, ','); + index++; + } + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + return list; + } + + @Override + public Object getValue() { + return this; + } + + @Override + public void setValue(Object value) { + if (this == value) + return; + FileScopeProblemPreference scope = (FileScopeProblemPreference) value; + setAttribute(INCLUSION, scope.getAttribute(INCLUSION)); + setAttribute(EXCLUSION, scope.getAttribute(EXCLUSION)); + this.resource = scope.getResource(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.cdt.codan.core.param.AbstractProblemPreference#clone() + */ + @Override + public Object clone() { + FileScopeProblemPreference scope = (FileScopeProblemPreference) super + .clone(); + scope.setValue(this); + return scope; + } + + /** + * @param file + * @return + */ + public boolean isInScope(IPath file) { + //System.err.println("test " + file + " " + exportValue()); + if (inclusion.length > 0) { + if (!matchesFilter(file, inclusion)) + return false; + } + if (exclusion.length > 0) { + if (matchesFilter(file, exclusion)) + return false; + } + return true; + } + + /** + * @param resourcePath + * @param inclusion2 + * @return + */ + private boolean matchesFilter(IPath resourcePath, IPath[] paths) { + char[] path = resourcePath.toString().toCharArray(); + for (int i = 0, length = paths.length; i < length; i++) { + char[] pattern = paths[i].toString().toCharArray(); + if (CharOperation.pathMatch(pattern, path, true, '/')) { + return true; + } + } + return false; + } + + public static boolean isExcludedPath(IPath resourcePath, IPath[] paths) { + char[] path = resourcePath.toString().toCharArray(); + for (int i = 0, length = paths.length; i < length; i++) { + char[] pattern = paths[i].toString().toCharArray(); + if (CharOperation.pathMatch(pattern, path, true, '/')) { + return true; + } + } + return false; + } +} diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/IProblemPreferenceDescriptor.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/IProblemPreferenceDescriptor.java index a0b8700474e..53f1440ec4c 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/IProblemPreferenceDescriptor.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/IProblemPreferenceDescriptor.java @@ -36,7 +36,8 @@ public interface IProblemPreferenceDescriptor extends Cloneable { TYPE_BOOLEAN("boolean"), //$NON-NLS-1$ TYPE_FILE("file"), //$NON-NLS-1$ TYPE_LIST("list"), //$NON-NLS-1$ - TYPE_MAP("map"); //$NON-NLS-1$ + TYPE_MAP("map"), //$NON-NLS-1$ + TYPE_CUSTOM("custom"); //$NON-NLS-1$ private String literal; private PreferenceType(String literal) { @@ -75,8 +76,7 @@ public interface IProblemPreferenceDescriptor extends Cloneable { return TYPE_LIST; if (value instanceof Map) return TYPE_MAP; - throw new IllegalArgumentException( - "Cannot determine type for " + value.getClass()); //$NON-NLS-1$ + return TYPE_CUSTOM; } } diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/ListProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/ListProblemPreference.java index 48d1c15414f..e9ccf74a618 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/ListProblemPreference.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/ListProblemPreference.java @@ -157,11 +157,16 @@ public class ListProblemPreference extends AbstractProblemPreference implements list.remove(index); } - @SuppressWarnings("unchecked") @Override public Object clone() { ListProblemPreference list1 = (ListProblemPreference) super.clone(); - list1.list = (ArrayList) list.clone(); + list1.list = new ArrayList(); + list1.setChildDescriptor((IProblemPreference) getChildDescriptor() + .clone()); + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + Object value = iterator.next(); + list1.addChildValue(value); + } return list1; } @@ -177,6 +182,7 @@ public class ListProblemPreference extends AbstractProblemPreference implements return buf.toString() + ")"; //$NON-NLS-1$ } + @Override public void importValue(String str) { StreamTokenizer tokenizer = getImportTokenizer(str); try { diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/MapProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/MapProblemPreference.java index 26469db51ac..acf474e6f19 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/MapProblemPreference.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/MapProblemPreference.java @@ -98,11 +98,15 @@ public class MapProblemPreference extends AbstractProblemPreference implements hash.remove(key); } - @SuppressWarnings("unchecked") @Override public Object clone() { MapProblemPreference map = (MapProblemPreference) super.clone(); - map.hash = (LinkedHashMap) hash.clone(); + map.hash = new LinkedHashMap(); + for (Iterator iterator = hash.keySet().iterator(); iterator + .hasNext();) { + String key = iterator.next(); + map.hash.put(key, (IProblemPreference) hash.get(key).clone()); + } return map; } @@ -119,6 +123,7 @@ public class MapProblemPreference extends AbstractProblemPreference implements return buf.toString() + "}"; //$NON-NLS-1$ } + @Override public void importValue(String str) { StreamTokenizer tokenizer = getImportTokenizer(str); try { diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CharOperation.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CharOperation.java new file mode 100644 index 00000000000..8134610d0f7 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CharOperation.java @@ -0,0 +1,2703 @@ +package org.eclipse.cdt.codan.internal.core; + +/** + * This class is a collection of helper methods to manipulate char arrays. + * + * @since 2.1 + */ +public final class CharOperation { + /** + * Constant for an empty char array + */ + public static final char[] NO_CHAR = new char[0]; + /** + * Constant for an empty char array with two dimensions. + */ + public static final char[][] NO_CHAR_CHAR = new char[0][]; + + /** + * Answers a new array with appending the suffix character at the end of the + * array. + *
+ *
+ * For example:
+ *
    + *
  1. +	 * array = { 'a', 'b' }
    +	 * suffix = 'c'
    +	 * => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 * array = null
    +	 * suffix = 'c'
    +	 * => result = { 'c' }
    +	 * 
  4. + *
+ * + * @param array + * the array that is concanated with the suffix character + * @param suffix + * the suffix character + * @return the new array + */ + public static final char[] append(char[] array, char suffix) { + if (array == null) + return new char[] { suffix }; + int length = array.length; + System.arraycopy(array, 0, array = new char[length + 1], 0, length); + array[length] = suffix; + return array; + } + + /** + * Append the given subarray to the target array starting at the given index + * in the target array. + * The start of the subarray is inclusive, the end is exclusive. + * Answers a new target array if it needs to grow, otherwise answers the + * same target array. + *
+ * For example:
+ *
    + *
  1. +	 * target = { 'a', 'b', '0' }
    +	 * index = 2
    +	 * array = { 'c', 'd' }
    +	 * start = 0
    +	 * end = 1
    +	 * => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 * target = { 'a', 'b' }
    +	 * index = 2
    +	 * array = { 'c', 'd' }
    +	 * start = 0
    +	 * end = 1
    +	 * => result = { 'a', 'b' , 'c', '0', '0' , '0' } (new array)
    +	 * 
  4. + *
  5. +	 * target = { 'a', 'b', 'c' }
    +	 * index = 1
    +	 * array = { 'c', 'd', 'e', 'f' }
    +	 * start = 1
    +	 * end = 4
    +	 * => result = { 'a', 'd' , 'e', 'f', '0', '0', '0', '0' } (new array)
    +	 * 
  6. + *
+ * + * @param target + * the given target + * @param index + * the given index + * @param array + * the given array + * @param start + * the given start index + * @param end + * the given end index + * + * @return the new array + * @throws NullPointerException + * if the target array is null + */ + public static final char[] append(char[] target, int index, char[] array, + int start, int end) { + int targetLength = target.length; + int subLength = end - start; + int newTargetLength = subLength + index; + if (newTargetLength > targetLength) { + System.arraycopy(target, 0, target = new char[newTargetLength * 2], + 0, index); + } + System.arraycopy(array, start, target, index, subLength); + return target; + } + + /** + * Answers the concatenation of the two arrays. It answers null if the two + * arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = null
    +	 * => result = null
    +	 * 
    + *
  2. + *
  3. +	 * first = { { ' a' } }
    +	 * second = null
    +	 * => result = { { ' a' } }
    +	 * 
    + *
  4. + *
  5. +	 * first = null
    +	 * second = { { ' a' } }
    +	 * => result = { { ' a' } }
    +	 * 
    + *
  6. + *
  7. +	 * first = { { ' b' } }
    +	 * second = { { ' a' } }
    +	 * => result = { { ' b' }, { ' a' } }
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array to concatenate + * @param second + * the second array to concatenate + * @return the concatenation of the two arrays, or null if the two arrays + * are null. + */ + public static final char[][] arrayConcat(char[][] first, char[][] second) { + if (first == null) + return second; + if (second == null) + return first; + int length1 = first.length; + int length2 = second.length; + char[][] result = new char[length1 + length2][]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + return result; + } + + /** + * Answers a new array adding the second array at the end of first array. + * It answers null if the first and second are null. + * If the first array is null, then a new array char[][] is created with + * second. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = { 'a' }
    +	 * => result = { { ' a' } }
    +	 * 
    + *
  2. +	 * first = { { ' a' } }
    +	 * second = null
    +	 * => result = { { ' a' } }
    +	 * 
    + *
  3. + *
  4. +	 * first = { { ' a' } }
    +	 * second = { ' b' }
    +	 * => result = { { ' a' } , { ' b' } }
    +	 * 
    + *
  5. + *
+ * + * @param first + * the first array to concatenate + * @param second + * the array to add at the end of the first array + * @return a new array adding the second array at the end of first array, or + * null if the two arrays are null. + */ + public static final char[][] arrayConcat(char[][] first, char[] second) { + if (second == null) + return first; + if (first == null) + return new char[][] { second }; + int length = first.length; + char[][] result = new char[length + 1][]; + System.arraycopy(first, 0, result, 0, length); + result[length] = second; + return result; + } + + /** + * Compares the contents of the two arrays array and prefix. Returns + *
    + *
  • zero if the array starts with the prefix contents
  • + *
  • the difference between the first two characters that are not equal + *
  • + *
  • one if array length is lower than the prefix length and that the + * prefix starts with the + * array contents.
  • + *
+ *

+ * For example: + *

    + *
  1. +	 * array = null
    +	 * prefix = null
    +	 * => result = NullPointerException
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'a', 'b', 'c', 'd', 'e' }
    +	 * prefix = { 'a', 'b', 'c'}
    +	 * => result = 0
    +	 * 
    + *
  4. + *
  5. +	 * array = { 'a', 'b', 'c', 'd', 'e' }
    +	 * prefix = { 'a', 'B', 'c'}
    +	 * => result = 32
    +	 * 
    + *
  6. + *
  7. +	 * array = { 'd', 'b', 'c', 'd', 'e' }
    +	 * prefix = { 'a', 'b', 'c'}
    +	 * => result = 3
    +	 * 
    + *
  8. + *
  9. +	 * array = { 'a', 'b', 'c', 'd', 'e' }
    +	 * prefix = { 'd', 'b', 'c'}
    +	 * => result = -3
    +	 * 
    + *
  10. + *
  11. +	 * array = { 'a', 'a', 'c', 'd', 'e' }
    +	 * prefix = { 'a', 'e', 'c'}
    +	 * => result = -4
    +	 * 
    + *
  12. + *
+ *

+ * + * @param array + * the given array + * @param prefix + * the given prefix + * @return the result of the comparison + * @exception NullPointerException + * if either array or prefix is null + */ + public static final int compareWith(char[] array, char[] prefix) { + int arrayLength = array.length; + int prefixLength = prefix.length; + int min = Math.min(arrayLength, prefixLength); + int i = 0; + while (min-- != 0) { + char c1 = array[i]; + char c2 = prefix[i++]; + if (c1 != c2) + return c1 - c2; + } + if (prefixLength == i) + return 0; + return 1; + } + + /** + * Answers the concatenation of the two arrays. It answers null if the two + * arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = { 'a' }
    +	 * => result = { ' a' }
    +	 * 
    + *
  2. + *
  3. +	 * first = { ' a' }
    +	 * second = null
    +	 * => result = { ' a' }
    +	 * 
    + *
  4. + *
  5. +	 * first = { ' a' }
    +	 * second = { ' b' }
    +	 * => result = { ' a' , ' b' }
    +	 * 
    + *
  6. + *
+ * + * @param first + * the first array to concatenate + * @param second + * the second array to concatenate + * @return the concatenation of the two arrays, or null if the two arrays + * are null. + */ + public static final char[] concat(char[] first, char[] second) { + if (first == null) + return second; + if (second == null) + return first; + int length1 = first.length; + int length2 = second.length; + char[] result = new char[length1 + length2]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + return result; + } + + /** + * Answers the concatenation of the three arrays. It answers null if the + * three arrays are null. + * If first is null, it answers the concatenation of second and third. + * If second is null, it answers the concatenation of first and third. + * If third is null, it answers the concatenation of first and second. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = { 'a' }
    +	 * third = { 'b' }
    +	 * => result = { ' a', 'b' }
    +	 * 
    + *
  2. + *
  3. +	 * first = { 'a' }
    +	 * second = null
    +	 * third = { 'b' }
    +	 * => result = { ' a', 'b' }
    +	 * 
    + *
  4. + *
  5. +	 * first = { 'a' }
    +	 * second = { 'b' }
    +	 * third = null
    +	 * => result = { ' a', 'b' }
    +	 * 
    + *
  6. + *
  7. +	 * first = null
    +	 * second = null
    +	 * third = null
    +	 * => result = null
    +	 * 
    + *
  8. + *
  9. +	 * first = { 'a' }
    +	 * second = { 'b' }
    +	 * third = { 'c' }
    +	 * => result = { 'a', 'b', 'c' }
    +	 * 
    + *
  10. + *
+ * + * @param first + * the first array to concatenate + * @param second + * the second array to concatenate + * @param third + * the third array to concatenate + * + * @return the concatenation of the three arrays, or null if the three + * arrays are null. + */ + public static final char[] concat(char[] first, char[] second, char[] third) { + if (first == null) + return concat(second, third); + if (second == null) + return concat(first, third); + if (third == null) + return concat(first, second); + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + char[] result = new char[length1 + length2 + length3]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + System.arraycopy(third, 0, result, length1 + length2, length3); + return result; + } + + /** + * Answers the concatenation of the two arrays inserting the separator + * character between the two arrays. + * It answers null if the two arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = { 'a' }
    +	 * separator = '/'
    +	 * => result = { ' a' }
    +	 * 
    + *
  2. + *
  3. +	 * first = { ' a' }
    +	 * second = null
    +	 * separator = '/'
    +	 * => result = { ' a' }
    +	 * 
    + *
  4. + *
  5. +	 * first = { ' a' }
    +	 * second = { ' b' }
    +	 * separator = '/'
    +	 * => result = { ' a' , '/', 'b' }
    +	 * 
    + *
  6. + *
+ * + * @param first + * the first array to concatenate + * @param second + * the second array to concatenate + * @param separator + * the character to insert + * @return the concatenation of the two arrays inserting the separator + * character + * between the two arrays , or null if the two arrays are null. + */ + public static final char[] concat(char[] first, char[] second, + char separator) { + if (first == null) + return second; + if (second == null) + return first; + int length1 = first.length; + if (length1 == 0) + return second; + int length2 = second.length; + if (length2 == 0) + return first; + char[] result = new char[length1 + length2 + 1]; + System.arraycopy(first, 0, result, 0, length1); + result[length1] = separator; + System.arraycopy(second, 0, result, length1 + 1, length2); + return result; + } + + /** + * Answers the concatenation of the three arrays inserting the sep1 + * character between the + * two arrays and sep2 between the last two. + * It answers null if the three arrays are null. + * If the first array is null, then it answers the concatenation of second + * and third inserting + * the sep2 character between them. + * If the second array is null, then it answers the concatenation of first + * and third inserting + * the sep1 character between them. + * If the third array is null, then it answers the concatenation of first + * and second inserting + * the sep1 character between them. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * sep1 = '/'
    +	 * second = { 'a' }
    +	 * sep2 = ':'
    +	 * third = { 'b' }
    +	 * => result = { ' a' , ':', 'b' }
    +	 * 
    + *
  2. + *
  3. +	 * first = { 'a' }
    +	 * sep1 = '/'
    +	 * second = null
    +	 * sep2 = ':'
    +	 * third = { 'b' }
    +	 * => result = { ' a' , '/', 'b' }
    +	 * 
    + *
  4. + *
  5. +	 * first = { 'a' }
    +	 * sep1 = '/'
    +	 * second = { 'b' }
    +	 * sep2 = ':'
    +	 * third = null
    +	 * => result = { ' a' , '/', 'b' }
    +	 * 
    + *
  6. + *
  7. +	 * first = { 'a' }
    +	 * sep1 = '/'
    +	 * second = { 'b' }
    +	 * sep2 = ':'
    +	 * third = { 'c' }
    +	 * => result = { ' a' , '/', 'b' , ':', 'c' }
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array to concatenate + * @param sep1 + * the character to insert + * @param second + * the second array to concatenate + * @param sep2 + * the character to insert + * @param third + * the second array to concatenate + * @return the concatenation of the three arrays inserting the sep1 + * character between the + * two arrays and sep2 between the last two. + */ + public static final char[] concat(char[] first, char sep1, char[] second, + char sep2, char[] third) { + if (first == null) + return concat(second, third, sep2); + if (second == null) + return concat(first, third, sep1); + if (third == null) + return concat(first, second, sep1); + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + char[] result = new char[length1 + length2 + length3 + 2]; + System.arraycopy(first, 0, result, 0, length1); + result[length1] = sep1; + System.arraycopy(second, 0, result, length1 + 1, length2); + result[length1 + length2 + 1] = sep2; + System.arraycopy(third, 0, result, length1 + length2 + 2, length3); + return result; + } + + /** + * Answers a new array with prepending the prefix character and appending + * the suffix + * character at the end of the array. If array is null, it answers a new + * array containing the + * prefix and the suffix characters. + *
+ *
+ * For example:
+ *
    + *
  1. +	 * prefix = 'a'
    +	 * array = { 'b' }
    +	 * suffix = 'c'
    +	 * => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 * prefix = 'a'
    +	 * array = null
    +	 * suffix = 'c'
    +	 * => result = { 'a', 'c' }
    +	 * 
  4. + *
+ * + * @param prefix + * the prefix character + * @param array + * the array that is concanated with the prefix and suffix + * characters + * @param suffix + * the suffix character + * @return the new array + */ + public static final char[] concat(char prefix, char[] array, char suffix) { + if (array == null) + return new char[] { prefix, suffix }; + int length = array.length; + char[] result = new char[length + 2]; + result[0] = prefix; + System.arraycopy(array, 0, result, 1, length); + result[length + 1] = suffix; + return result; + } + + /** + * Answers the concatenation of the given array parts using the given + * separator between each + * part and appending the given name at the end. + *
+ *
+ * For example:
+ *
    + *
  1. +	 * name = { 'c' }
    +	 * array = { { 'a' }, { 'b' } }
    +	 * separator = '.'
    +	 * => result = { 'a', '.', 'b' , '.', 'c' }
    +	 * 
    + *
  2. + *
  3. +	 * name = null
    +	 * array = { { 'a' }, { 'b' } }
    +	 * separator = '.'
    +	 * => result = { 'a', '.', 'b' }
    +	 * 
  4. + *
  5. +	 * name = { ' c' }
    +	 * array = null
    +	 * separator = '.'
    +	 * => result = { 'c' }
    +	 * 
  6. + *
+ * + * @param name + * the given name + * @param array + * the given array + * @param separator + * the given separator + * @return the concatenation of the given array parts using the given + * separator between each + * part and appending the given name at the end + */ + public static final char[] concatWith(char[] name, char[][] array, + char separator) { + int nameLength = name == null ? 0 : name.length; + if (nameLength == 0) + return concatWith(array, separator); + if (array == null) + return name; + final int length = array.length; + if (length == 0) + return name; + int size = nameLength; + int index = length; + while (--index >= 0) + if (array[index].length > 0) + size += array[index].length + 1; + char[] result = new char[size]; + index = size; + for (int i = length - 1; i >= 0; i--) { + int subLength = array[i].length; + if (subLength > 0) { + index -= subLength; + System.arraycopy(array[i], 0, result, index, subLength); + result[--index] = separator; + } + } + System.arraycopy(name, 0, result, 0, nameLength); + return result; + } + + /** + * Answers the concatenation of the given array parts using the given + * separator between each + * part and appending the given name at the end. + *
+ *
+ * For example:
+ *
    + *
  1. +	 * name = { 'c' }
    +	 * array = { { 'a' }, { 'b' } }
    +	 * separator = '.'
    +	 * => result = { 'a', '.', 'b' , '.', 'c' }
    +	 * 
    + *
  2. + *
  3. +	 * name = null
    +	 * array = { { 'a' }, { 'b' } }
    +	 * separator = '.'
    +	 * => result = { 'a', '.', 'b' }
    +	 * 
  4. + *
  5. +	 * name = { ' c' }
    +	 * array = null
    +	 * separator = '.'
    +	 * => result = { 'c' }
    +	 * 
  6. + *
+ * + * @param array + * the given array + * @param name + * the given name + * @param separator + * the given separator + * @return the concatenation of the given array parts using the given + * separator between each + * part and appending the given name at the end + */ + public static final char[] concatWith(char[][] array, char[] name, + char separator) { + int nameLength = name == null ? 0 : name.length; + if (nameLength == 0) + return concatWith(array, separator); + if (array == null) + return name; + final int length = array.length; + if (length == 0) + return name; + int size = nameLength; + int index = length; + while (--index >= 0) + if (array[index].length > 0) + size += array[index].length + 1; + char[] result = new char[size]; + index = 0; + for (int i = 0; i < length; i++) { + int subLength = array[i].length; + if (subLength > 0) { + System.arraycopy(array[i], 0, result, index, subLength); + index += subLength; + result[index++] = separator; + } + } + System.arraycopy(name, 0, result, index, nameLength); + return result; + } + + /** + * Answers the concatenation of the given array parts using the given + * separator between each part. + *
+ *
+ * For example:
+ *
    + *
  1. +	 * array = { { 'a' }, { 'b' } }
    +	 * separator = '.'
    +	 * => result = { 'a', '.', 'b' }
    +	 * 
    + *
  2. + *
  3. +	 * array = null
    +	 * separator = '.'
    +	 * => result = { }
    +	 * 
  4. + *
+ * + * @param array + * the given array + * @param separator + * the given separator + * @return the concatenation of the given array parts using the given + * separator between each part + */ + public static final char[] concatWith(char[][] array, char separator) { + if (array == null) + return CharOperation.NO_CHAR; + int length = array.length; + if (length == 0) + return CharOperation.NO_CHAR; + int size = length - 1; + int index = length; + while (--index >= 0) { + if (array[index].length == 0) + size--; + else + size += array[index].length; + } + if (size <= 0) + return CharOperation.NO_CHAR; + char[] result = new char[size]; + index = length; + while (--index >= 0) { + length = array[index].length; + if (length > 0) { + System.arraycopy(array[index], 0, result, (size -= length), + length); + if (--size >= 0) + result[size] = separator; + } + } + return result; + } + + /** + * Answers true if the array contains an occurrence of character, false + * otherwise. + * + *
+ *
+ * For example: + *
    + *
  1. +	 * character = 'c'
    +	 * array = { { ' a' }, { ' b' } }
    +	 * result => false
    +	 * 
    + *
  2. + *
  3. +	 * character = 'a'
    +	 * array = { { ' a' }, { ' b' } }
    +	 * result => true
    +	 * 
    + *
  4. + *
+ * + * @param character + * the character to search + * @param array + * the array in which the search is done + * @return true if the array contains an occurrence of character, false + * otherwise. + * @exception NullPointerException + * if array is null. + */ + public static final boolean contains(char character, char[][] array) { + for (int i = array.length; --i >= 0;) { + char[] subarray = array[i]; + for (int j = subarray.length; --j >= 0;) + if (subarray[j] == character) + return true; + } + return false; + } + + /** + * Answers true if the array contains an occurrence of character, false + * otherwise. + * + *
+ *
+ * For example: + *
    + *
  1. +	 * character = 'c'
    +	 * array = { ' b' }
    +	 * result => false
    +	 * 
    + *
  2. + *
  3. +	 * character = 'a'
    +	 * array = { ' a' , ' b' }
    +	 * result => true
    +	 * 
    + *
  4. + *
+ * + * @param character + * the character to search + * @param array + * the array in which the search is done + * @return true if the array contains an occurrence of character, false + * otherwise. + * @exception NullPointerException + * if array is null. + */ + public static final boolean contains(char character, char[] array) { + for (int i = array.length; --i >= 0;) + if (array[i] == character) + return true; + return false; + } + + /** + * Answers a deep copy of the toCopy array. + * + * @param toCopy + * the array to copy + * @return a deep copy of the toCopy array. + */ + public static final char[][] deepCopy(char[][] toCopy) { + int toCopyLength = toCopy.length; + char[][] result = new char[toCopyLength][]; + for (int i = 0; i < toCopyLength; i++) { + char[] toElement = toCopy[i]; + int toElementLength = toElement.length; + char[] resultElement = new char[toElementLength]; + System.arraycopy(toElement, 0, resultElement, 0, toElementLength); + result[i] = resultElement; + } + return result; + } + + /** + * Return true if array ends with the sequence of characters contained in + * toBeFound, + * otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { 'a', 'b', 'c', 'd' }
    +	 * toBeFound = { 'b', 'c' }
    +	 * result => false
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'a', 'b', 'c' }
    +	 * toBeFound = { 'b', 'c' }
    +	 * result => true
    +	 * 
    + *
  4. + *
+ * + * @param array + * the array to check + * @param toBeFound + * the array to find + * @return true if array ends with the sequence of characters contained in + * toBeFound, + * otherwise false. + * @exception NullPointerException + * if array is null or toBeFound is null + */ + public static final boolean endsWith(char[] array, char[] toBeFound) { + int i = toBeFound.length; + int j = array.length - i; + if (j < 0) + return false; + while (--i >= 0) + if (toBeFound[i] != array[i + j]) + return false; + return true; + } + + /** + * Answers true if the two arrays are identical character by character, + * otherwise false. + * The equality is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = null
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * first = { { } }
    +	 * second = null
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * first = { { 'a' } }
    +	 * second = { { 'a' } }
    +	 * result => true
    +	 * 
    + *
  6. + *
  7. +	 * first = { { 'A' } }
    +	 * second = { { 'a' } }
    +	 * result => false
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array + * @param second + * the second array + * @return true if the two arrays are identical character by character, + * otherwise false + */ + public static final boolean equals(char[][] first, char[][] second) { + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + for (int i = first.length; --i >= 0;) + if (!equals(first[i], second[i])) + return false; + return true; + } + + /** + * If isCaseSensite is true, answers true if the two arrays are identical + * character + * by character, otherwise false. + * If it is false, answers true if the two arrays are identical character by + * character without checking the case, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = null
    +	 * isCaseSensitive = true
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * first = { { } }
    +	 * second = null
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * first = { { 'A' } }
    +	 * second = { { 'a' } }
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  6. + *
  7. +	 * first = { { 'A' } }
    +	 * second = { { 'a' } }
    +	 * isCaseSensitive = false
    +	 * result => true
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array + * @param second + * the second array + * @param isCaseSensitive + * check whether or not the equality should be case sensitive + * @return true if the two arrays are identical character by character + * according to the value + * of isCaseSensitive, otherwise false + */ + public static final boolean equals(char[][] first, char[][] second, + boolean isCaseSensitive) { + if (isCaseSensitive) { + return equals(first, second); + } + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + for (int i = first.length; --i >= 0;) + if (!equals(first[i], second[i], false)) + return false; + return true; + } + + /** + * Answers true if the two arrays are identical character by character, + * otherwise false. + * The equality is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = null
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * first = { }
    +	 * second = null
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * first = { 'a' }
    +	 * second = { 'a' }
    +	 * result => true
    +	 * 
    + *
  6. + *
  7. +	 * first = { 'a' }
    +	 * second = { 'A' }
    +	 * result => false
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array + * @param second + * the second array + * @return true if the two arrays are identical character by character, + * otherwise false + */ + public static final boolean equals(char[] first, char[] second) { + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + for (int i = first.length; --i >= 0;) + if (first[i] != second[i]) + return false; + return true; + } + + /** + * If isCaseSensite is true, answers true if the two arrays are identical + * character + * by character, otherwise false. + * If it is false, answers true if the two arrays are identical character by + * character without checking the case, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 * first = null
    +	 * second = null
    +	 * isCaseSensitive = true
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * first = { }
    +	 * second = null
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * first = { 'A' }
    +	 * second = { 'a' }
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  6. + *
  7. +	 * first = { 'A' }
    +	 * second = { 'a' }
    +	 * isCaseSensitive = false
    +	 * result => true
    +	 * 
    + *
  8. + *
+ * + * @param first + * the first array + * @param second + * the second array + * @param isCaseSensitive + * check whether or not the equality should be case sensitive + * @return true if the two arrays are identical character by character + * according to the value + * of isCaseSensitive, otherwise false + */ + public static final boolean equals(char[] first, char[] second, + boolean isCaseSensitive) { + if (isCaseSensitive) { + return equals(first, second); + } + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + for (int i = first.length; --i >= 0;) + if (Character.toLowerCase(first[i]) != Character + .toLowerCase(second[i])) + return false; + return true; + } + + /** + * If isCaseSensite is true, the equality is case sensitive, otherwise it is + * case insensitive. + * + * Answers true if the name contains the fragment at the starting index + * startIndex, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 * fragment = { 'b', 'c' , 'd' }
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * startIndex = 1
    +	 * isCaseSensitive = true
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * fragment = { 'b', 'c' , 'd' }
    +	 * name = { 'a', 'b', 'C' , 'd' }
    +	 * startIndex = 1
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * fragment = { 'b', 'c' , 'd' }
    +	 * name = { 'a', 'b', 'C' , 'd' }
    +	 * startIndex = 0
    +	 * isCaseSensitive = false
    +	 * result => false
    +	 * 
    + *
  6. + *
  7. +	 * fragment = { 'b', 'c' , 'd' }
    +	 * name = { 'a', 'b'}
    +	 * startIndex = 0
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  8. + *
+ * + * @param fragment + * the fragment to check + * @param name + * the array to check + * @param startIndex + * the starting index + * @param isCaseSensitive + * check whether or not the equality should be case sensitive + * @return true if the name contains the fragment at the starting index + * startIndex according to the + * value of isCaseSensitive, otherwise false. + * @exception NullPointerException + * if fragment or name is null. + */ + public static final boolean fragmentEquals(char[] fragment, char[] name, + int startIndex, boolean isCaseSensitive) { + int max = fragment.length; + if (name.length < max + startIndex) + return false; + if (isCaseSensitive) { + for (int i = max; --i >= 0;) + // assumes the prefix is not larger than the name + if (fragment[i] != name[i + startIndex]) + return false; + return true; + } + for (int i = max; --i >= 0;) + // assumes the prefix is not larger than the name + if (Character.toLowerCase(fragment[i]) != Character + .toLowerCase(name[i + startIndex])) + return false; + return true; + } + + /** + * Answers a hashcode for the array + * + * @param array + * the array for which a hashcode is required + * @return the hashcode + * @exception NullPointerException + * if array is null + */ + public static final int hashCode(char[] array) { + int hash = 0; + int offset = 0; + int length = array.length; + if (length < 16) { + for (int i = length; i > 0; i--) + hash = (hash * 37) + array[offset++]; + } else { + // only sample some characters + int skip = length / 8; + for (int i = length; i > 0; i -= skip, offset += skip) + hash = (hash * 39) + array[offset]; + } + return hash & 0x7FFFFFFF; + } + + /** + * Answers true if c is a whitespace according to the JLS (\u000a, + * \u000c, \u000d, \u0009), otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 * c = ' '
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * c = '\u3000'
    +	 * result => false
    +	 * 
    + *
  4. + *
+ * + * @param c + * the character to check + * @return true if c is a whitespace according to the JLS, otherwise false. + */ + public static boolean isWhitespace(char c) { + switch (c) { + case 10: /* \ u000a: LINE FEED */ + case 12: /* \ u000c: FORM FEED */ + case 13: /* \ u000d: CARRIAGE RETURN */ + case 32: /* \ u0020: SPACE */ + case 9: /* \ u0009: HORIZONTAL TABULATION */ + return true; + default: + return false; + } + } + + /** + * Answers the first index in the array for which the corresponding + * character is + * equal to toBeFound. Answers -1 if no occurrence of this character is + * found. + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * result => 2
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'e'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * result => -1
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound + * the character to search + * @param array + * the array to be searched + * @return the first index in the array for which the corresponding + * character is + * equal to toBeFound, -1 otherwise + * @exception NullPointerException + * if array is null + */ + public static final int indexOf(char toBeFound, char[] array) { + for (int i = 0; i < array.length; i++) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the first index in the array for which the corresponding + * character is + * equal to toBeFound starting the search at index start. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * start = 2
    +	 * result => 2
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * start = 3
    +	 * result => -1
    +	 * 
    + *
  4. + *
  5. +	 * toBeFound = 'e'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * start = 1
    +	 * result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound + * the character to search + * @param array + * the array to be searched + * @param start + * the starting index + * @return the first index in the array for which the corresponding + * character is + * equal to toBeFound, -1 otherwise + * @exception NullPointerException + * if array is null + * @exception ArrayIndexOutOfBoundsException + * if start is lower than 0 + */ + public static final int indexOf(char toBeFound, char[] array, int start) { + for (int i = start; i < array.length; i++) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character + * is + * equal to toBeFound starting from the end of the array. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' , 'c', 'e' }
    +	 * result => 4
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'e'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * result => -1
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound + * the character to search + * @param array + * the array to be searched + * @return the last index in the array for which the corresponding character + * is + * equal to toBeFound starting from the end of the array, -1 + * otherwise + * @exception NullPointerException + * if array is null + */ + public static final int lastIndexOf(char toBeFound, char[] array) { + for (int i = array.length; --i >= 0;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character + * is + * equal to toBeFound stopping at the index startIndex. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * startIndex = 2
    +	 * result => 2
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd', 'e' }
    +	 * startIndex = 3
    +	 * result => -1
    +	 * 
    + *
  4. + *
  5. +	 * toBeFound = 'e'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * startIndex = 0
    +	 * result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound + * the character to search + * @param array + * the array to be searched + * @param startIndex + * the stopping index + * @return the last index in the array for which the corresponding character + * is + * equal to toBeFound stopping at the index startIndex, -1 otherwise + * @exception NullPointerException + * if array is null + * @exception ArrayIndexOutOfBoundsException + * if startIndex is lower than 0 + */ + public static final int lastIndexOf(char toBeFound, char[] array, + int startIndex) { + for (int i = array.length; --i >= startIndex;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character + * is + * equal to toBeFound starting from endIndex to startIndex. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * startIndex = 2
    +	 * endIndex = 2
    +	 * result => 2
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'c'
    +	 * array = { ' a', 'b', 'c', 'd', 'e' }
    +	 * startIndex = 3
    +	 * endIndex = 4
    +	 * result => -1
    +	 * 
    + *
  4. + *
  5. +	 * toBeFound = 'e'
    +	 * array = { ' a', 'b', 'c', 'd' }
    +	 * startIndex = 0
    +	 * endIndex = 3
    +	 * result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound + * the character to search + * @param array + * the array to be searched + * @param startIndex + * the stopping index + * @param endIndex + * the starting index + * @return the last index in the array for which the corresponding character + * is + * equal to toBeFound starting from endIndex to startIndex, -1 + * otherwise + * @exception NullPointerException + * if array is null + * @exception ArrayIndexOutOfBoundsException + * if endIndex is greater or equals to array length or + * starting is lower than 0 + */ + public static final int lastIndexOf(char toBeFound, char[] array, + int startIndex, int endIndex) { + for (int i = endIndex; --i >= startIndex;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last portion of a name given a separator. + *
+ *
+ * For example, + *
+	 * lastSegment("java.lang.Object".toCharArray(),'.') --> Object
+	 * 
+ * + * @param array + * the array + * @param separator + * the given separator + * @return the last portion of a name given a separator + * @exception NullPointerException + * if array is null + */ + final static public char[] lastSegment(char[] array, char separator) { + int pos = lastIndexOf(separator, array); + if (pos < 0) + return array; + return subarray(array, pos + 1, array.length); + } + + /** + * Answers true if the pattern matches the given name, false otherwise. This + * char[] pattern matching + * accepts wild-cards '*' and '?'. + * + * When not case sensitive, the pattern is assumed to already be lowercased, + * the + * name will be lowercased character per character as comparing. + * If name is null, the answer is false. + * If pattern is null, the answer is true if name is not null. + *
+ *
+ * For example: + *
    + *
  1. +	 * pattern = { '?', 'b', '*' }
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * isCaseSensitive = true
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * pattern = { '?', 'b', '?' }
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
  5. +	 * pattern = { 'b', '*' }
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  6. + *
+ * + * @param pattern + * the given pattern + * @param name + * the given name + * @param isCaseSensitive + * flag to know whether or not the matching should be case + * sensitive + * @return true if the pattern matches the given name, false otherwise + */ + public static final boolean match(char[] pattern, char[] name, + boolean isCaseSensitive) { + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + return match(pattern, 0, pattern.length, name, 0, name.length, + isCaseSensitive, true); + } + + /** + * Answers true if the a sub-pattern matches the subpart of the given name, + * false otherwise. + * char[] pattern matching, accepting wild-cards '*' and '?'. Can match only + * subset of name/pattern. + * end positions are non-inclusive. + * The subpattern is defined by the patternStart and pattternEnd positions. + * When not case sensitive, the pattern is assumed to already be lowercased, + * the + * name will be lowercased character per character as comparing. + *
+ *
+ * For example: + *
    + *
  1. +	 * pattern = { '?', 'b', '*' }
    +	 * patternStart = 1
    +	 * patternEnd = 3
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * nameStart = 1
    +	 * nameEnd = 4
    +	 * isCaseSensitive = true
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * pattern = { '?', 'b', '*' }
    +	 * patternStart = 1
    +	 * patternEnd = 2
    +	 * name = { 'a', 'b', 'c' , 'd' }
    +	 * nameStart = 1
    +	 * nameEnd = 2
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
+ * + * @param pattern + * the given pattern + * @param patternStart + * the given pattern start + * @param patternEnd + * the given pattern end + * @param name + * the given name + * @param nameStart + * the given name start + * @param nameEnd + * the given name end + * @param isCaseSensitive + * flag to know if the matching should be case sensitive + * @return true if the a sub-pattern matches the subpart of the given name, + * false otherwise + */ + public static final boolean match(char[] pattern, int patternStart, + int patternEnd, char[] name, int nameStart, int nameEnd, + boolean isCaseSensitive) { + return match(pattern, patternStart, patternEnd, name, nameStart, + nameEnd, isCaseSensitive, false); + } + + public static final boolean match(char[] pattern, int patternStart, + int patternEnd, char[] name, int nameStart, int nameEnd, + boolean isCaseSensitive, boolean allowEscaping) { + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + int iPattern = patternStart; + int iName = nameStart; + if (patternEnd < 0) + patternEnd = pattern.length; + if (nameEnd < 0) + nameEnd = name.length; + /* check first segment */ + char patternChar = 0; + boolean isEscaped = false; + while ((iPattern < patternEnd) + && ((patternChar = pattern[iPattern]) != '*' || (patternChar == '*' && isEscaped))) { + if (allowEscaping && pattern[iPattern] == '\\' && !isEscaped) { + iPattern++; + isEscaped = true; + continue; + } else + isEscaped = false; + if (iName == nameEnd) + return false; + if (patternChar != (isCaseSensitive ? name[iName] : Character + .toLowerCase(name[iName])) && patternChar != '?') { + return false; + } + iName++; + iPattern++; + patternChar = 0; + } + /* check sequence of star+segment */ + int segmentStart; + if (patternChar == '*') { + segmentStart = ++iPattern; // skip star + } else { + segmentStart = 0; // force iName check + } + int prefixStart = iName; + checkSegment: while (iName < nameEnd) { + if (iPattern == patternEnd) { + iPattern = segmentStart; // mismatch - restart current segment + iName = ++prefixStart; + continue checkSegment; + } + /* segment is ending */ + if ((patternChar = pattern[iPattern]) == '*') { + segmentStart = ++iPattern; // skip start + if (segmentStart == patternEnd) { + return true; + } + prefixStart = iName; + continue checkSegment; + } + /* check current name character */ + if ((isCaseSensitive ? name[iName] : Character + .toLowerCase(name[iName])) != patternChar + && patternChar != '?') { + iPattern = segmentStart; // mismatch - restart current segment + iName = ++prefixStart; + continue checkSegment; + } + iName++; + iPattern++; + } + return (segmentStart == patternEnd) + || (iName == nameEnd && iPattern == patternEnd) + || (iPattern == patternEnd - 1 && pattern[iPattern] == '*'); + } + + /** + * Answers true if the pattern matches the filepath using the pathSepatator, + * false otherwise. + * + * Path char[] pattern matching, accepting wild-cards '**', '*' and '?' + * (using Ant directory tasks + * conventions, also see + * "http://jakarta.apache.org/ant/manual/dirtasks.html#defaultexcludes"). + * Path pattern matching is enhancing regular pattern matching in supporting + * extra rule where '**' represent + * any folder combination. + * Special rules: + * - foo\ is equivalent to foo\** + * - *.java is equivalent to **\*.java + * When not case sensitive, the pattern is assumed to already be lowercased, + * the + * name will be lowercased character per character as comparing. + * + * @param pattern + * the given pattern + * @param filepath + * the given path + * @param isCaseSensitive + * to find out whether or not the matching should be case + * sensitive + * @param pathSeparator + * the given path separator + * @return true if the pattern matches the filepath using the pathSepatator, + * false otherwise + */ + public static final boolean pathMatch(char[] pattern, char[] filepath, + boolean isCaseSensitive, char pathSeparator) { + if (filepath == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + // special case: pattern foo is equivalent to **\foo (not absolute) + boolean freeLeadingDoubleStar; + // offsets inside pattern + int pSegmentStart, pLength = pattern.length; + freeLeadingDoubleStar = (pattern[0] != pathSeparator); + if (freeLeadingDoubleStar) { + pSegmentStart = 0; + } else { + pSegmentStart = 1; + } + int pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart + 1); + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + // special case: pattern foo\ is equivalent to foo\** + boolean freeTrailingDoubleStar = pattern[pLength - 1] == pathSeparator; + // offsets inside filepath + int fSegmentStart, fLength = filepath.length; + if (filepath[0] != pathSeparator) { + fSegmentStart = 0; + } else { + fSegmentStart = 1; + } + if (fSegmentStart != pSegmentStart) { + return false; // both must start with a separator or none. + } + int fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, + fSegmentStart + 1); + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + // first segments + while (pSegmentStart < pLength + && !freeLeadingDoubleStar + && !(pSegmentEnd == pLength && freeTrailingDoubleStar || (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' && pattern[pSegmentStart + 1] == '*'))) { + if (fSegmentStart >= fLength) + return false; + if (!CharOperation.match(pattern, pSegmentStart, pSegmentEnd, + filepath, fSegmentStart, fSegmentEnd, isCaseSensitive)) { + return false; + } + // jump to next segment + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, + fSegmentStart = fSegmentEnd + 1); + // skip separator + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + } + /* check sequence of doubleStar+segment */ + int pSegmentRestart; + if ((pSegmentStart >= pLength && freeTrailingDoubleStar) + || (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' && pattern[pSegmentStart + 1] == '*')) { + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + pSegmentRestart = pSegmentStart; + } else { + if (pSegmentStart >= pLength) + return fSegmentStart >= fLength; // true if filepath is done too. + pSegmentRestart = 0; // force fSegmentStart check + } + int fSegmentRestart = fSegmentStart; + checkSegment: while (fSegmentStart < fLength) { + if (pSegmentStart >= pLength) { + if (freeTrailingDoubleStar) + return true; + // mismatch - restart current path segment + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentRestart); + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + fSegmentRestart = CharOperation.indexOf(pathSeparator, + filepath, fSegmentRestart + 1); + // skip separator + if (fSegmentRestart < 0) { + fSegmentRestart = fLength; + } else { + fSegmentRestart++; + } + fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, + fSegmentStart = fSegmentRestart); + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + continue checkSegment; + } + /* path segment is ending */ + if (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' + && pattern[pSegmentStart + 1] == '*') { + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + pSegmentRestart = pSegmentStart; + fSegmentRestart = fSegmentStart; + if (pSegmentStart >= pLength) + return true; + continue checkSegment; + } + /* chech current path segment */ + if (!CharOperation.match(pattern, pSegmentStart, pSegmentEnd, + filepath, fSegmentStart, fSegmentEnd, isCaseSensitive)) { + // mismatch - restart current path segment + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentRestart); + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + fSegmentRestart = CharOperation.indexOf(pathSeparator, + filepath, fSegmentRestart + 1); + // skip separator + if (fSegmentRestart < 0) { + fSegmentRestart = fLength; + } else { + fSegmentRestart++; + } + fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, + fSegmentStart = fSegmentRestart); + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + continue checkSegment; + } + // jump to next segment + pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, + fSegmentStart = fSegmentEnd + 1); + // skip separator + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + } + return (pSegmentRestart >= pSegmentEnd) + || (fSegmentStart >= fLength && pSegmentStart >= pLength) + || (pSegmentStart == pLength - 2 + && pattern[pSegmentStart] == '*' && pattern[pSegmentStart + 1] == '*') + || (pSegmentStart == pLength && freeTrailingDoubleStar); + } + + /** + * Answers the number of occurrences of the given character in the given + * array, 0 if any. + * + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'b'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => 3
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'c'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => 0
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound + * the given character + * @param array + * the given array + * @return the number of occurrences of the given character in the given + * array, 0 if any + * @exception NullPointerException + * if array is null + */ + public static final int occurencesOf(char toBeFound, char[] array) { + int count = 0; + for (char element : array) + if (toBeFound == element) + count++; + return count; + } + + /** + * Answers the number of occurrences of the given character in the given + * array starting + * at the given index, 0 if any. + * + *
+ *
+ * For example: + *
    + *
  1. +	 * toBeFound = 'b'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * start = 2
    +	 * result => 2
    +	 * 
    + *
  2. + *
  3. +	 * toBeFound = 'c'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * start = 0
    +	 * result => 0
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound + * the given character + * @param array + * the given array + * @return the number of occurrences of the given character in the given + * array, 0 if any + * @exception NullPointerException + * if array is null + * @exception ArrayIndexOutOfBoundsException + * if start is lower than 0 + */ + public static final int occurencesOf(char toBeFound, char[] array, int start) { + int count = 0; + for (int i = start; i < array.length; i++) + if (toBeFound == array[i]) + count++; + return count; + } + + /** + * Answers true if the given name starts with the given prefix, false + * otherwise. + * The comparison is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 * prefix = { 'a' , 'b' }
    +	 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * prefix = { 'a' , 'c' }
    +	 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => false
    +	 * 
    + *
  4. + *
+ * + * @param prefix + * the given prefix + * @param name + * the given name + * @return true if the given name starts with the given prefix, false + * otherwise + * @exception NullPointerException + * if the given name is null or if the given prefix is null + */ + public static final boolean prefixEquals(char[] prefix, char[] name) { + int max = prefix.length; + if (name.length < max) + return false; + for (int i = max; --i >= 0;) + // assumes the prefix is not larger than the name + if (prefix[i] != name[i]) + return false; + return true; + } + + /** + * Answers true if the given name starts with the given prefix, false + * otherwise. + * isCaseSensitive is used to find out whether or not the comparison should + * be case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 * prefix = { 'a' , 'B' }
    +	 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * isCaseSensitive = false
    +	 * result => true
    +	 * 
    + *
  2. + *
  3. +	 * prefix = { 'a' , 'B' }
    +	 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * isCaseSensitive = true
    +	 * result => false
    +	 * 
    + *
  4. + *
+ * + * @param prefix + * the given prefix + * @param name + * the given name + * @param isCaseSensitive + * to find out whether or not the comparison should be case + * sensitive + * @return true if the given name starts with the given prefix, false + * otherwise + * @exception NullPointerException + * if the given name is null or if the given prefix is null + */ + public static final boolean prefixEquals(char[] prefix, char[] name, + boolean isCaseSensitive) { + int max = prefix.length; + if (name.length < max) + return false; + if (isCaseSensitive) { + for (int i = max; --i >= 0;) + // assumes the prefix is not larger than the name + if (prefix[i] != name[i]) + return false; + return true; + } + for (int i = max; --i >= 0;) + // assumes the prefix is not larger than the name + if (Character.toLowerCase(prefix[i]) != Character + .toLowerCase(name[i])) + return false; + return true; + } + + /** + * Replace all occurrence of the character to be replaced with the + * remplacement character in the + * given array. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * toBeReplaced = 'b'
    +	 * replacementChar = 'a'
    +	 * result => No returned value, but array is now equals to { 'a' , 'a', 'a',
    +	 * 'a', 'a', 'a' }
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * toBeReplaced = 'c'
    +	 * replacementChar = 'a'
    +	 * result => No returned value, but array is now equals to { 'a' , 'b', 'b',
    +	 * 'a', 'b', 'a' }
    +	 * 
    + *
  4. + *
+ * + * @param array + * the given array + * @param toBeReplaced + * the character to be replaced + * @param replacementChar + * the replacement character + * @exception NullPointerException + * if the given array is null + */ + public static final void replace(char[] array, char toBeReplaced, + char replacementChar) { + if (toBeReplaced != replacementChar) { + for (int i = 0, max = array.length; i < max; i++) { + if (array[i] == toBeReplaced) + array[i] = replacementChar; + } + } + } + + /** + * Answers a new array of characters with substitutions. No side-effect is + * operated on the original + * array, in case no substitution happened, then the result is the same as + * the + * original one. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * toBeReplaced = { 'b' }
    +	 * replacementChar = { 'a', 'a' }
    +	 * result => { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' }
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * toBeReplaced = { 'c' }
    +	 * replacementChar = { 'a' }
    +	 * result => { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * 
    + *
  4. + *
+ * + * @param array + * the given array + * @param toBeReplaced + * characters to be replaced + * @param replacementChars + * the replacement characters + * @return a new array of characters with substitutions or the given array + * if none + * @exception NullPointerException + * if the given array is null + */ + public static final char[] replace(char[] array, char[] toBeReplaced, + char[] replacementChars) { + int max = array.length; + int replacedLength = toBeReplaced.length; + int replacementLength = replacementChars.length; + int[] starts = new int[5]; + int occurrenceCount = 0; + if (!equals(toBeReplaced, replacementChars)) { + next: for (int i = 0; i < max; i++) { + int j = 0; + while (j < replacedLength) { + if (i + j == max) + continue next; + if (array[i + j] != toBeReplaced[j++]) + continue next; + } + if (occurrenceCount == starts.length) { + System.arraycopy(starts, 0, + starts = new int[occurrenceCount * 2], 0, + occurrenceCount); + } + starts[occurrenceCount++] = i; + } + } + if (occurrenceCount == 0) + return array; + char[] result = new char[max + occurrenceCount + * (replacementLength - replacedLength)]; + int inStart = 0, outStart = 0; + for (int i = 0; i < occurrenceCount; i++) { + int offset = starts[i] - inStart; + System.arraycopy(array, inStart, result, outStart, offset); + inStart += offset; + outStart += offset; + System.arraycopy(replacementChars, 0, result, outStart, + replacementLength); + inStart += replacedLength; + outStart += replacementLength; + } + System.arraycopy(array, inStart, result, outStart, max - inStart); + return result; + } + + /** + * Return a new array which is the split of the given array using the given + * divider and triming each subarray to remove + * whitespaces equals to ' '. + *
+ *
+ * For example: + *
    + *
  1. +	 * divider = 'b'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 * divider = 'c'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  4. + *
  5. +	 * divider = 'b'
    +	 * array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' }
    +	 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
    +	 * 
    + *
  6. + *
  7. +	 * divider = 'c'
    +	 * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
    +	 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  8. + *
+ * + * @param divider + * the given divider + * @param array + * the given array + * @return a new array which is the split of the given array using the given + * divider and triming each subarray to remove + * whitespaces equals to ' ' + */ + public static final char[][] splitAndTrimOn(char divider, char[] array) { + if (array == null) + return NO_CHAR_CHAR; + final int length = array.length; + if (length == 0) + return NO_CHAR_CHAR; + int wordCount = 1; + for (int i = 0; i < length; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = 0, currentWord = 0; + for (int i = 0; i < length; i++) { + if (array[i] == divider) { + int start = last, end = i - 1; + while (start < i && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord] = new char[end - start + 1]; + System.arraycopy(array, start, split[currentWord++], 0, end + - start + 1); + last = i + 1; + } + } + int start = last, end = length - 1; + while (start < length && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord] = new char[end - start + 1]; + System.arraycopy(array, start, split[currentWord++], 0, end - start + 1); + return split; + } + + /** + * Return a new array which is the split of the given array using the given + * divider. + *
+ *
+ * For example: + *
    + *
  1. +	 * divider = 'b'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 * divider = 'c'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  4. + *
  5. +	 * divider = 'c'
    +	 * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
    +	 * result => { { ' ', 'a', 'b', 'b', 'a', 'b', 'a', ' ' } }
    +	 * 
    + *
  6. + *
+ * + * @param divider + * the given divider + * @param array + * the given array + * @return a new array which is the split of the given array using the given + * divider + */ + public static final char[][] splitOn(char divider, char[] array) { + if (array == null) + return NO_CHAR_CHAR; + final int length = array.length; + if (length == 0) + return NO_CHAR_CHAR; + int wordCount = 1; + for (int i = 0; i < length; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = 0, currentWord = 0; + for (int i = 0; i < length; i++) { + if (array[i] == divider) { + split[currentWord] = new char[i - last]; + System.arraycopy(array, last, split[currentWord++], 0, i - last); + last = i + 1; + } + } + split[currentWord] = new char[length - last]; + System.arraycopy(array, last, split[currentWord], 0, length - last); + return split; + } + + /** + * Return a new array which is the split of the given array using the given + * divider. The given end + * is exclusive and the given start is inclusive. + *
+ *
+ * For example: + *
    + *
  1. +	 * divider = 'b'
    +	 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * start = 2
    +	 * end = 5
    +	 * result => { { }, { }, { 'a' } }
    +	 * 
    + *
  2. + *
+ * + * @param divider + * the given divider + * @param array + * the given array + * @param start + * the given starting index + * @param end + * the given ending index + * @return a new array which is the split of the given array using the given + * divider + * @exception ArrayIndexOutOfBoundsException + * if start is lower than 0 or end is greater than the array + * length + */ + public static final char[][] splitOn(char divider, char[] array, int start, + int end) { + if (array == null) + return NO_CHAR_CHAR; + final int length = array.length; + if (length == 0 || start > end) + return NO_CHAR_CHAR; + int wordCount = 1; + for (int i = start; i < end; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = start, currentWord = 0; + for (int i = start; i < end; i++) { + if (array[i] == divider) { + split[currentWord] = new char[i - last]; + System.arraycopy(array, last, split[currentWord++], 0, i - last); + last = i + 1; + } + } + split[currentWord] = new char[end - last]; + System.arraycopy(array, last, split[currentWord], 0, end - last); + return split; + } + + /** + * Answers a new array which is a copy of the given array starting at the + * given start and + * ending at the given end. The given start is inclusive and the given end + * is exclusive. + * Answers null if start is greater than end, if start is lower than 0 or if + * end is greater + * than the length of the given array. If end equals -1, it is converted to + * the array length. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { { 'a' } , { 'b' } }
    +	 * start = 0
    +	 * end = 1
    +	 * result => { { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 * array = { { 'a' } , { 'b' } }
    +	 * start = 0
    +	 * end = -1
    +	 * result => { { 'a' }, { 'b' } }
    +	 * 
    + *
  4. + *
+ * + * @param array + * the given array + * @param start + * the given starting index + * @param end + * the given ending index + * @return a new array which is a copy of the given array starting at the + * given start and + * ending at the given end + * @exception NullPointerException + * if the given array is null + */ + public static final char[][] subarray(char[][] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + char[][] result = new char[end - start][]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + + /** + * Answers a new array which is a copy of the given array starting at the + * given start and + * ending at the given end. The given start is inclusive and the given end + * is exclusive. + * Answers null if start is greater than end, if start is lower than 0 or if + * end is greater + * than the length of the given array. If end equals -1, it is converted to + * the array length. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { 'a' , 'b' }
    +	 * start = 0
    +	 * end = 1
    +	 * result => { 'a' }
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'a', 'b' }
    +	 * start = 0
    +	 * end = -1
    +	 * result => { 'a' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param array + * the given array + * @param start + * the given starting index + * @param end + * the given ending index + * @return a new array which is a copy of the given array starting at the + * given start and + * ending at the given end + * @exception NullPointerException + * if the given array is null + */ + public static final char[] subarray(char[] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + char[] result = new char[end - start]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + + /** + * Answers the result of a char[] conversion to lowercase. Answers null if + * the given chars array is null. + *
+ * NOTE: If no conversion was necessary, then answers back the argument one. + *
+ *
+ * For example: + *
    + *
  1. +	 * chars = { 'a' , 'b' }
    +	 * result => { 'a' , 'b' }
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'A', 'b' }
    +	 * result => { 'a' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param chars + * the chars to convert + * @return the result of a char[] conversion to lowercase + */ + final static public char[] toLowerCase(char[] chars) { + if (chars == null) + return null; + int length = chars.length; + char[] lowerChars = null; + for (int i = 0; i < length; i++) { + char c = chars[i]; + char lc = Character.toLowerCase(c); + if ((c != lc) || (lowerChars != null)) { + if (lowerChars == null) { + System.arraycopy(chars, 0, lowerChars = new char[length], + 0, i); + } + lowerChars[i] = lc; + } + } + return lowerChars == null ? chars : lowerChars; + } + + /** + * Answers a new array removing leading and trailing spaces (' '). Answers + * the given array if there is no + * space characters to remove. + *
+ *
+ * For example: + *
    + *
  1. +	 * chars = { ' ', 'a' , 'b', ' ', ' ' }
    +	 * result => { 'a' , 'b' }
    +	 * 
    + *
  2. + *
  3. +	 * array = { 'A', 'b' }
    +	 * result => { 'A' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param chars + * the given array + * @return a new array removing leading and trailing spaces (' ') + */ + final static public char[] trim(char[] chars) { + if (chars == null) + return null; + int start = 0, length = chars.length, end = length - 1; + while (start < length && chars[start] == ' ') { + start++; + } + while (end > start && chars[end] == ' ') { + end--; + } + if (start != 0 || end != length - 1) { + return subarray(chars, start, end + 1); + } + return chars; + } + + /** + * Answers a string which is the concatenation of the given array using the + * '.' as a separator. + *
+ *
+ * For example: + *
    + *
  1. +	 * array = { { 'a' } , { 'b' } }
    +	 * result => "a.b"
    +	 * 
    + *
  2. + *
  3. +	 * array = { { ' ', 'a' } , { 'b' } }
    +	 * result => " a.b"
    +	 * 
    + *
  4. + *
+ * + * @param array + * the given array + * @return a string which is the concatenation of the given array using the + * '.' as a separator + */ + final static public String toString(char[][] array) { + char[] result = concatWith(array, '.'); + return new String(result); + } +} diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/model/CodanProblem.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/model/CodanProblem.java index e2187f5c306..f4c00f31666 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/model/CodanProblem.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/model/CodanProblem.java @@ -70,7 +70,11 @@ public class CodanProblem implements IProblemWorkingCopy { */ @Override public Object clone() throws CloneNotSupportedException { - return super.clone(); + CodanProblem prob = (CodanProblem) super.clone(); + if (preference != null) { + prob.preference = (IProblemPreference) preference.clone(); + } + return prob; } public void setPreference(IProblemPreference value) { diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java index 004530e7495..5b3134e5d34 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java @@ -41,6 +41,40 @@ public class CodanUIMessages extends NLS { public static String Job_TitleRunningAnalysis; public static String ParametersComposite_NewValue; public static String ParametersComposite_None; + // + public static String ExclusionInclusionDialog_title; + public static String ExclusionInclusionDialog_description; + public static String ExclusionInclusionDialog_description2; + public static String ExclusionInclusionDialog_exclusion_pattern_label; + public static String ExclusionInclusionDialog_inclusion_pattern_label; + public static String ExclusionInclusionDialog_inclusion_pattern_add; + public static String ExclusionInclusionDialog_inclusion_pattern_add_multiple; + public static String ExclusionInclusionDialog_inclusion_pattern_remove; + public static String ExclusionInclusionDialog_inclusion_pattern_edit; + public static String ExclusionInclusionDialog_exclusion_pattern_add; + public static String ExclusionInclusionDialog_exclusion_pattern_add_multiple; + public static String ExclusionInclusionDialog_exclusion_pattern_remove; + public static String ExclusionInclusionDialog_exclusion_pattern_edit; + public static String ExclusionInclusionDialog_ChooseExclusionPattern_title; + public static String ExclusionInclusionDialog_ChooseExclusionPattern_description; + public static String ExclusionInclusionDialog_ChooseInclusionPattern_title; + public static String ExclusionInclusionDialog_ChooseInclusionPattern_description; + public static String ExclusionInclusionEntryDialog_exclude_add_title; + public static String ExclusionInclusionEntryDialog_exclude_edit_title; + public static String ExclusionInclusionEntryDialog_exclude_description; + public static String ExclusionInclusionEntryDialog_exclude_pattern_label; + public static String ExclusionInclusionEntryDialog_include_add_title; + public static String ExclusionInclusionEntryDialog_include_edit_title; + public static String ExclusionInclusionEntryDialog_include_description; + public static String ExclusionInclusionEntryDialog_include_pattern_label; + public static String ExclusionInclusionEntryDialog_pattern_button; + public static String ExclusionInclusionEntryDialog_error_empty; + public static String ExclusionInclusionEntryDialog_error_notrelative; + public static String ExclusionInclusionEntryDialog_error_exists; + public static String ExclusionInclusionEntryDialog_ChooseExclusionPattern_title; + public static String ExclusionInclusionEntryDialog_ChooseExclusionPattern_description; + public static String ExclusionInclusionEntryDialog_ChooseInclusionPattern_title; + public static String ExclusionInclusionEntryDialog_ChooseInclusionPattern_description; static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, CodanUIMessages.class); diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemDialog.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemDialog.java index 6f24b251b96..f0f9f714b34 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemDialog.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemDialog.java @@ -13,6 +13,8 @@ package org.eclipse.cdt.codan.internal.ui.dialogs; import org.eclipse.cdt.codan.core.model.IProblem; import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; +import org.eclipse.cdt.codan.internal.ui.widgets.CustomizeProblemComposite; +import org.eclipse.core.resources.IResource; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; @@ -27,27 +29,37 @@ import org.eclipse.swt.widgets.Shell; public class CustomizeProblemDialog extends TitleAreaDialog { private CustomizeProblemComposite comp; private IProblem problem; + private IResource resource; /** * @param parentShell * @param selectedProblem + * @param iResource */ - public CustomizeProblemDialog(Shell parentShell, IProblem selectedProblem) { + public CustomizeProblemDialog(Shell parentShell, IProblem selectedProblem, + IResource resource) { super(parentShell); this.problem = selectedProblem; + this.resource = resource; setShellStyle(getShellStyle() | SWT.RESIZE); } /** * Stores edit values into problem working copy - * @param problem - problem working copy + * + * @param problem + * - problem working copy */ public void save(IProblemWorkingCopy problem) { comp.save(problem); } - - /* (non-Javadoc) - * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite) + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse + * .swt.widgets.Composite) */ @Override protected Control createDialogArea(Composite parent) { @@ -55,14 +67,16 @@ public class CustomizeProblemDialog extends TitleAreaDialog { setTitle(problem.getName()); setMessage(CodanUIMessages.CustomizeProblemDialog_Message); Composite area = (Composite) super.createDialogArea(parent); - comp = new CustomizeProblemComposite(area, problem); + comp = new CustomizeProblemComposite(area, problem, resource); GridData ld = new GridData(GridData.FILL_BOTH); - ld.minimumHeight=300; + ld.minimumHeight = 300; comp.setLayoutData(ld); return area; } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.eclipse.jface.dialogs.Dialog#okPressed() */ @Override diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ExclusionInclusionEntryDialog.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ExclusionInclusionEntryDialog.java new file mode 100644 index 00000000000..ba35b389fb9 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ExclusionInclusionEntryDialog.java @@ -0,0 +1,274 @@ +package org.eclipse.cdt.codan.internal.ui.dialogs; + +import java.text.MessageFormat; +import java.util.List; + +import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; +import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; +import org.eclipse.cdt.codan.internal.ui.widgets.BasicElementLabels; +import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; +import org.eclipse.cdt.internal.ui.dialogs.TypedElementSelectionValidator; +import org.eclipse.cdt.internal.ui.dialogs.TypedViewerFilter; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IStringButtonAdapter; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringButtonDialogField; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.dialogs.StatusDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; +import org.eclipse.ui.dialogs.ISelectionStatusValidator; +import org.eclipse.ui.model.WorkbenchContentProvider; +import org.eclipse.ui.model.WorkbenchLabelProvider; +import org.eclipse.ui.views.navigator.ResourceComparator; + +public class ExclusionInclusionEntryDialog extends StatusDialog { + private StringButtonDialogField fExclusionPatternDialog; + private StatusInfo fExclusionPatternStatus; + private IContainer fCurrSourceFolder; + private String fExclusionPattern; + private List fExistingPatterns; + private boolean fIsExclusion; + + public ExclusionInclusionEntryDialog(Shell parent, boolean isExclusion, + String patternToEdit, List existingPatterns, + FileScopeProblemPreference entryToEdit) { + super(parent); + fIsExclusion = isExclusion; + fExistingPatterns = existingPatterns; + String title, message; + if (isExclusion) { + if (patternToEdit == null) { + title = CodanUIMessages.ExclusionInclusionEntryDialog_exclude_add_title; + } else { + title = CodanUIMessages.ExclusionInclusionEntryDialog_exclude_edit_title; + } + message = MessageFormat + .format(CodanUIMessages.ExclusionInclusionEntryDialog_exclude_pattern_label, + BasicElementLabels.getPathLabel( + entryToEdit.getPath(), false)); + } else { + if (patternToEdit == null) { + title = CodanUIMessages.ExclusionInclusionEntryDialog_include_add_title; + } else { + title = CodanUIMessages.ExclusionInclusionEntryDialog_include_edit_title; + } + message = MessageFormat + .format(CodanUIMessages.ExclusionInclusionEntryDialog_include_pattern_label, + BasicElementLabels.getPathLabel( + entryToEdit.getPath(), false)); + } + setTitle(title); + if (patternToEdit != null) { + fExistingPatterns.remove(patternToEdit); + } + IProject currProject = entryToEdit.getProject(); + IWorkspaceRoot root = currProject != null ? currProject.getWorkspace() + .getRoot() : ResourcesPlugin.getWorkspace().getRoot(); + IResource res = root.findMember(entryToEdit.getPath()); + if (res instanceof IContainer) { + fCurrSourceFolder = (IContainer) res; + } + fExclusionPatternStatus = new StatusInfo(); + ExclusionPatternAdapter adapter = new ExclusionPatternAdapter(); + fExclusionPatternDialog = new StringButtonDialogField(adapter); + fExclusionPatternDialog.setLabelText(message); + fExclusionPatternDialog + .setButtonLabel(CodanUIMessages.ExclusionInclusionEntryDialog_pattern_button); + fExclusionPatternDialog.setDialogFieldListener(adapter); + fExclusionPatternDialog.enableButton(fCurrSourceFolder != null); + if (patternToEdit == null) { + fExclusionPatternDialog.setText(""); //$NON-NLS-1$ + } else { + fExclusionPatternDialog.setText(patternToEdit.toString()); + } + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + int widthHint = convertWidthInCharsToPixels(60); + Composite inner = new Composite(composite, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 2; + inner.setLayout(layout); + Label description = new Label(inner, SWT.WRAP); + if (fIsExclusion) { + description + .setText(CodanUIMessages.ExclusionInclusionEntryDialog_exclude_description); + } else { + description + .setText(CodanUIMessages.ExclusionInclusionEntryDialog_include_description); + } + GridData gd = new GridData(); + gd.horizontalSpan = 2; + gd.widthHint = convertWidthInCharsToPixels(80); + description.setLayoutData(gd); + fExclusionPatternDialog.doFillIntoGrid(inner, 3); + LayoutUtil.setWidthHint(fExclusionPatternDialog.getLabelControl(null), + widthHint); + LayoutUtil.setHorizontalSpan( + fExclusionPatternDialog.getLabelControl(null), 2); + LayoutUtil.setWidthHint(fExclusionPatternDialog.getTextControl(null), + widthHint); + LayoutUtil.setHorizontalGrabbing(fExclusionPatternDialog + .getTextControl(null)); + fExclusionPatternDialog.postSetFocusOnDialogField(parent.getDisplay()); + applyDialogFont(composite); + return composite; + } + + // -------- ExclusionPatternAdapter -------- + private class ExclusionPatternAdapter implements IDialogFieldListener, + IStringButtonAdapter { + // -------- IDialogFieldListener + public void dialogFieldChanged(DialogField field) { + doStatusLineUpdate(); + } + + public void changeControlPressed(DialogField field) { + doChangeControlPressed(); + } + } + + protected void doChangeControlPressed() { + IPath pattern = chooseExclusionPattern(); + if (pattern != null) { + fExclusionPatternDialog.setText(pattern.toString()); + } + } + + protected void doStatusLineUpdate() { + checkIfPatternValid(); + updateStatus(fExclusionPatternStatus); + } + + protected void checkIfPatternValid() { + String pattern = fExclusionPatternDialog.getText().trim(); + if (pattern.length() == 0) { + fExclusionPatternStatus + .setError(CodanUIMessages.ExclusionInclusionEntryDialog_error_empty); + return; + } + IPath path = new Path(pattern); + if (path.isAbsolute() || path.getDevice() != null) { + fExclusionPatternStatus + .setError(CodanUIMessages.ExclusionInclusionEntryDialog_error_notrelative); + return; + } + if (fExistingPatterns.contains(pattern)) { + fExclusionPatternStatus + .setError(CodanUIMessages.ExclusionInclusionEntryDialog_error_exists); + return; + } + fExclusionPattern = pattern; + fExclusionPatternStatus.setOK(); + } + + public String getExclusionPattern() { + return fExclusionPattern; + } + + /* + * @see org.eclipse.jface.window.Window#configureShell(Shell) + */ + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + } + + // ---------- util method ------------ + private IPath chooseExclusionPattern() { + String title, message; + if (fIsExclusion) { + title = CodanUIMessages.ExclusionInclusionEntryDialog_ChooseExclusionPattern_title; + message = CodanUIMessages.ExclusionInclusionEntryDialog_ChooseExclusionPattern_description; + } else { + title = CodanUIMessages.ExclusionInclusionEntryDialog_ChooseInclusionPattern_title; + message = CodanUIMessages.ExclusionInclusionEntryDialog_ChooseInclusionPattern_description; + } + IPath initialPath = new Path(fExclusionPatternDialog.getText()); + IPath[] res = chooseExclusionPattern(getShell(), fCurrSourceFolder, + title, message, initialPath, false); + if (res == null) { + return null; + } + return res[0]; + } + + public static IPath[] chooseExclusionPattern(Shell shell, + IContainer currentSourceFolder, String title, String message, + IPath initialPath, boolean multiSelection) { + Class[] acceptedClasses = new Class[] { IFolder.class, IFile.class, + IProject.class }; + ISelectionStatusValidator validator = new TypedElementSelectionValidator( + acceptedClasses, multiSelection); + ViewerFilter filter = new TypedViewerFilter(acceptedClasses); + ILabelProvider lp = new WorkbenchLabelProvider(); + ITreeContentProvider cp = new WorkbenchContentProvider(); + IResource initialElement = null; + if (initialPath != null) { + IContainer curr = currentSourceFolder; + int nSegments = initialPath.segmentCount(); + for (int i = 0; i < nSegments; i++) { + IResource elem = curr.findMember(initialPath.segment(i)); + if (elem != null) { + initialElement = elem; + } + if (elem instanceof IContainer) { + curr = (IContainer) elem; + } else { + break; + } + } + } + ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog( + shell, lp, cp); + dialog.setTitle(title); + dialog.setValidator(validator); + dialog.setMessage(message); + dialog.addFilter(filter); + dialog.setInput(currentSourceFolder); + dialog.setInitialSelection(initialElement); + dialog.setComparator(new ResourceComparator(ResourceComparator.NAME)); + dialog.setHelpAvailable(false); + if (dialog.open() == Window.OK) { + Object[] objects = dialog.getResult(); + int existingSegments = currentSourceFolder.getFullPath() + .segmentCount(); + IPath[] resArr = new IPath[objects.length]; + for (int i = 0; i < objects.length; i++) { + IResource currRes = (IResource) objects[i]; + IPath path = currRes.getFullPath() + .removeFirstSegments(existingSegments).makeRelative(); + if (currRes instanceof IContainer) { + path = path.addTrailingSeparator(); + } + resArr[i] = path; + } + return resArr; + } + return null; + } +} diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/messages.properties b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/messages.properties index ddcfdfbfa73..37060839973 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/messages.properties +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/messages.properties @@ -23,3 +23,53 @@ OverlayPage_Use_Project_Settings=Use pr&oject settings OverlayPage_Configure_Workspace_Settings=&Configure Workspace Settings... PropertyStore_Cannot_write_resource_property=Cannot write resource property +# ------- ExclusionInclusionDialog ------- + +ExclusionInclusionDialog_title=Inclusion and Exclusion Patterns +ExclusionInclusionDialog_description=Included and excluded resources for ''{0}''. +ExclusionInclusionDialog_description2=Add or remove inclusion and exclusion patterns to resources for which problem is reported + +ExclusionInclusionDialog_exclusion_pattern_label=E&xclusion patterns: +ExclusionInclusionDialog_inclusion_pattern_label=I&nclusion patterns: + +ExclusionInclusionDialog_inclusion_pattern_add=A&dd... +ExclusionInclusionDialog_inclusion_pattern_add_multiple=Add &Multiple... +ExclusionInclusionDialog_inclusion_pattern_remove=&Remove +ExclusionInclusionDialog_inclusion_pattern_edit=&Edit... + +ExclusionInclusionDialog_exclusion_pattern_add=&Add... +ExclusionInclusionDialog_exclusion_pattern_add_multiple=Add M&ultiple... +ExclusionInclusionDialog_exclusion_pattern_remove=Rem&ove +ExclusionInclusionDialog_exclusion_pattern_edit=Edi&t... + +ExclusionInclusionDialog_ChooseExclusionPattern_title=Exclusion Pattern Selection +ExclusionInclusionDialog_ChooseExclusionPattern_description=&Choose folders or files to exclude: + +ExclusionInclusionDialog_ChooseInclusionPattern_title=Inclusion Pattern Selection +ExclusionInclusionDialog_ChooseInclusionPattern_description=&Choose folders or files to include: + +# ------- ExclusionInclusionEntryDialog ------- + +ExclusionInclusionEntryDialog_exclude_add_title=Add Exclusion Pattern +ExclusionInclusionEntryDialog_exclude_edit_title=Edit Exclusion Pattern +ExclusionInclusionEntryDialog_exclude_description=Enter a pattern for excluding files from the source folder. Allowed wildcards are '*', '?' and '**'. Examples: 'src/util/a*.c', 'src/util/', '**/Test*'. + +ExclusionInclusionEntryDialog_exclude_pattern_label=E&xclusion pattern (Path relative to ''{0}''): + +ExclusionInclusionEntryDialog_include_add_title=Add Inclusion Pattern +ExclusionInclusionEntryDialog_include_edit_title=Edit Inclusion Pattern +ExclusionInclusionEntryDialog_include_description=Enter a pattern for including files to the source folder. Allowed wildcards are '*', '?' and '**'. Examples: 'src/util/a*.c', 'src/util/', '**/Test*'. + +ExclusionInclusionEntryDialog_include_pattern_label=I&nclusion pattern (Path relative to ''{0}''): + +ExclusionInclusionEntryDialog_pattern_button=Bro&wse... + +ExclusionInclusionEntryDialog_error_empty=Enter a pattern. +ExclusionInclusionEntryDialog_error_notrelative=Pattern must be a relative path. +ExclusionInclusionEntryDialog_error_exists=Pattern already exists. + +ExclusionInclusionEntryDialog_ChooseExclusionPattern_title=Exclusion Pattern Selection +ExclusionInclusionEntryDialog_ChooseExclusionPattern_description=&Choose a folder or file to exclude: + +ExclusionInclusionEntryDialog_ChooseInclusionPattern_title=Inclusion Pattern Selection +ExclusionInclusionEntryDialog_ChooseInclusionPattern_description=&Choose a folder or file to include: \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/CodanPreferencePage.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/CodanPreferencePage.java index 3a7330065f0..1c436dde97d 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/CodanPreferencePage.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/CodanPreferencePage.java @@ -279,7 +279,7 @@ public class CodanPreferencePage extends FieldEditorOverlayPage implements */ protected void openCustomizeDialog() { CustomizeProblemDialog d = new CustomizeProblemDialog(getShell(), - selectedProblem); + selectedProblem, (IResource) getElement()); d.open(); } } \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/FileScopePreferencePage.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/FileScopePreferencePage.java new file mode 100644 index 00000000000..ed78a3ce8ff --- /dev/null +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/FileScopePreferencePage.java @@ -0,0 +1,302 @@ +package org.eclipse.cdt.codan.internal.ui.preferences; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; +import org.eclipse.cdt.codan.internal.ui.CodanUIActivator; +import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; +import org.eclipse.cdt.codan.internal.ui.dialogs.ExclusionInclusionEntryDialog; +import org.eclipse.cdt.codan.internal.ui.widgets.BasicElementLabels; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IListAdapter; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.ListDialogField; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +public class FileScopePreferencePage extends PreferencePage { + private ListDialogField fInclusionPatternList; + private ListDialogField fExclusionPatternList; + private FileScopeProblemPreference fCurrElement; + private IProject fCurrProject; + private IContainer fCurrSourceFolder; + private static final int IDX_ADD = 0; + private static final int IDX_ADD_MULTIPLE = 1; + private static final int IDX_EDIT = 2; + private static final int IDX_REMOVE = 4; + + public FileScopePreferencePage(FileScopeProblemPreference entryToEdit) { + setTitle(CodanUIMessages.ExclusionInclusionDialog_title); + setDescription(CodanUIMessages.ExclusionInclusionDialog_description2); + fCurrElement = entryToEdit; + fCurrProject = entryToEdit.getProject(); + IWorkspaceRoot root = fCurrProject != null ? fCurrProject + .getWorkspace().getRoot() : ResourcesPlugin.getWorkspace() + .getRoot(); + IResource res = root.findMember(entryToEdit.getPath()); + if (res instanceof IContainer) { + fCurrSourceFolder = (IContainer) res; + } + if (res == null) + fCurrSourceFolder = root; + String excLabel = CodanUIMessages.ExclusionInclusionDialog_exclusion_pattern_label; + ImageDescriptor excDescriptor = null; //JavaPluginImages.DESC_OBJS_EXCLUSION_FILTER_ATTRIB; + String[] excButtonLabels = new String[] { + CodanUIMessages.ExclusionInclusionDialog_exclusion_pattern_add, + CodanUIMessages.ExclusionInclusionDialog_exclusion_pattern_add_multiple, + CodanUIMessages.ExclusionInclusionDialog_exclusion_pattern_edit, + null, + CodanUIMessages.ExclusionInclusionDialog_exclusion_pattern_remove }; + String incLabel = CodanUIMessages.ExclusionInclusionDialog_inclusion_pattern_label; + ImageDescriptor incDescriptor = null; //JavaPluginImages.DESC_OBJS_INCLUSION_FILTER_ATTRIB; + String[] incButtonLabels = new String[] { + CodanUIMessages.ExclusionInclusionDialog_inclusion_pattern_add, + CodanUIMessages.ExclusionInclusionDialog_inclusion_pattern_add_multiple, + CodanUIMessages.ExclusionInclusionDialog_inclusion_pattern_edit, + null, + CodanUIMessages.ExclusionInclusionDialog_inclusion_pattern_remove }; + fExclusionPatternList = createListContents(entryToEdit, + FileScopeProblemPreference.EXCLUSION, excLabel, null, + excButtonLabels); + fInclusionPatternList = createListContents(entryToEdit, + FileScopeProblemPreference.INCLUSION, incLabel, null, + incButtonLabels); + } + + @Override + protected Control createContents(Composite parent) { + Composite inner = new Composite(parent, SWT.NONE); + inner.setFont(parent.getFont()); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 2; + inner.setLayout(layout); + inner.setLayoutData(new GridData(GridData.FILL_BOTH)); + fInclusionPatternList.doFillIntoGrid(inner, 3); + LayoutUtil.setHorizontalSpan( + fInclusionPatternList.getLabelControl(null), 2); + LayoutUtil.setHorizontalGrabbing(fInclusionPatternList + .getListControl(null)); + fExclusionPatternList.doFillIntoGrid(inner, 3); + LayoutUtil.setHorizontalSpan( + fExclusionPatternList.getLabelControl(null), 2); + LayoutUtil.setHorizontalGrabbing(fExclusionPatternList + .getListControl(null)); + setControl(inner); + Dialog.applyDialogFont(inner); + return inner; + } + + private static class ExclusionInclusionLabelProvider extends LabelProvider { + private Image fElementImage; + + public ExclusionInclusionLabelProvider(String descriptorPath) { + if (descriptorPath != null) { + ImageDescriptor d = CodanUIActivator + .getImageDescriptor(descriptorPath); + } + fElementImage = null; // XXX + } + + @Override + public Image getImage(Object element) { + return fElementImage; + } + + @Override + public String getText(Object element) { + return BasicElementLabels.getFilePattern((String) element); + } + } + + private ListDialogField createListContents( + FileScopeProblemPreference entryToEdit, String key, String label, + String descriptor, String[] buttonLabels) { + ExclusionPatternAdapter adapter = new ExclusionPatternAdapter(); + ListDialogField patternList = new ListDialogField(adapter, + buttonLabels, new ExclusionInclusionLabelProvider(descriptor)); + patternList.setDialogFieldListener(adapter); + patternList.setLabelText(label); + patternList.enableButton(IDX_EDIT, false); + IPath[] pattern = entryToEdit.getAttribute(key); + ArrayList elements = new ArrayList(pattern.length); + for (int i = 0; i < pattern.length; i++) { + String patternName = pattern[i].toString(); + if (patternName.length() > 0) + elements.add(patternName); + } + patternList.setElements(elements); + patternList.selectFirstElement(); + patternList.enableButton(IDX_ADD_MULTIPLE, fCurrSourceFolder != null); + patternList.setViewerComparator(new ViewerComparator()); + return patternList; + } + + protected void doCustomButtonPressed(ListDialogField field, int index) { + if (index == IDX_ADD) { + addEntry(field); + } else if (index == IDX_EDIT) { + editEntry(field); + } else if (index == IDX_ADD_MULTIPLE) { + addMultipleEntries(field); + } else if (index == IDX_REMOVE) { + field.removeElements(field.getSelectedElements()); + } + updateStatus(); + } + + private void updateStatus() { + fCurrElement.setAttribute(FileScopeProblemPreference.INCLUSION, + getInclusionPattern()); + fCurrElement.setAttribute(FileScopeProblemPreference.EXCLUSION, + getExclusionPattern()); + } + + protected void doDoubleClicked(ListDialogField field) { + editEntry(field); + updateStatus(); + } + + protected void doSelectionChanged(ListDialogField field) { + List selected = field.getSelectedElements(); + field.enableButton(IDX_EDIT, canEdit(selected)); + } + + private boolean canEdit(List selected) { + return selected.size() == 1; + } + + private void editEntry(ListDialogField field) { + List selElements = field.getSelectedElements(); + if (selElements.size() != 1) { + return; + } + List existing = field.getElements(); + String entry = (String) selElements.get(0); + ExclusionInclusionEntryDialog dialog = new ExclusionInclusionEntryDialog( + getShell(), isExclusion(field), entry, existing, fCurrElement); + if (dialog.open() == Window.OK) { + field.replaceElement(entry, dialog.getExclusionPattern()); + } + } + + private boolean isExclusion(ListDialogField field) { + return field == fExclusionPatternList; + } + + private void addEntry(ListDialogField field) { + List existing = field.getElements(); + ExclusionInclusionEntryDialog dialog = new ExclusionInclusionEntryDialog( + getShell(), isExclusion(field), null, existing, fCurrElement); + if (dialog.open() == Window.OK) { + field.addElement(dialog.getExclusionPattern()); + } + } + + // -------- ExclusionPatternAdapter -------- + private class ExclusionPatternAdapter implements IListAdapter, + IDialogFieldListener { + /** + * @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IListAdapter#customButtonPressed(org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField, + * int) + */ + public void customButtonPressed(ListDialogField field, int index) { + doCustomButtonPressed(field, index); + } + + /** + * @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IListAdapter#selectionChanged(org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField) + */ + public void selectionChanged(ListDialogField field) { + doSelectionChanged(field); + } + + /** + * @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IListAdapter#doubleClicked(org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField) + */ + public void doubleClicked(ListDialogField field) { + doDoubleClicked(field); + } + + /** + * @see org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener#dialogFieldChanged(org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField) + */ + public void dialogFieldChanged(DialogField field) { + } + } + + protected void doStatusLineUpdate() { + } + + protected void checkIfPatternValid() { + } + + private IPath[] getPattern(ListDialogField field) { + Object[] arr = field.getElements().toArray(); + Arrays.sort(arr); + IPath[] res = new IPath[arr.length]; + for (int i = 0; i < res.length; i++) { + res[i] = new Path((String) arr[i]); + } + return res; + } + + public IPath[] getExclusionPattern() { + return getPattern(fExclusionPatternList); + } + + public IPath[] getInclusionPattern() { + return getPattern(fInclusionPatternList); + } + + /* + * @see org.eclipse.jface.window.Window#configureShell(Shell) + */ + protected void configureShell(Shell newShell) { + } + + private void addMultipleEntries(ListDialogField field) { + String title, message; + if (isExclusion(field)) { + title = CodanUIMessages.ExclusionInclusionDialog_ChooseExclusionPattern_title; + message = CodanUIMessages.ExclusionInclusionDialog_ChooseExclusionPattern_description; + } else { + title = CodanUIMessages.ExclusionInclusionDialog_ChooseInclusionPattern_title; + message = CodanUIMessages.ExclusionInclusionDialog_ChooseInclusionPattern_description; + } + IPath[] res = ExclusionInclusionEntryDialog.chooseExclusionPattern( + getShell(), fCurrSourceFolder, title, message, null, true); + if (res != null) { + for (int i = 0; i < res.length; i++) { + field.addElement(res[i].toString()); + } + } + } + + @Override + public void noDefaultAndApplyButton() { + super.noDefaultAndApplyButton(); + } +} diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/BasicElementLabels.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/BasicElementLabels.java new file mode 100644 index 00000000000..e0c7ddce310 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/BasicElementLabels.java @@ -0,0 +1,147 @@ +package org.eclipse.cdt.codan.internal.ui.widgets; + +import java.io.File; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.osgi.util.TextProcessor; +import org.eclipse.ui.IWorkingSet; + +/** + * A label provider for basic elements like paths. The label provider will make + * sure that the labels are correctly + * shown in RTL environments. + * + * @since 3.4 + */ +public class BasicElementLabels { + // TextProcessor delimiters + private static final String CODE_DELIMITERS = TextProcessor + .getDefaultDelimiters() + "<>()?,{}+-*!%=^|&;[]~"; //$NON-NLS-1$ + private static final String FILE_PATTERN_DELIMITERS = TextProcessor + .getDefaultDelimiters() + "*.?"; //$NON-NLS-1$ + private static final String URL_DELIMITERS = TextProcessor + .getDefaultDelimiters() + ":@?-"; //$NON-NLS-1$ + + /** + * Returns the label of a path. + * + * @param path + * the path + * @param isOSPath + * if true, the path represents an OS path, if + * false it is a workspace path. + * @return the label of the path to be used in the UI. + */ + public static String getPathLabel(IPath path, boolean isOSPath) { + String label; + if (isOSPath) { + label = path.toOSString(); + } else { + label = path.makeRelative().toString(); + } + return markLTR(label); + } + + /** + * Returns the label of the path of a file. + * + * @param file + * the file + * @return the label of the file path to be used in the UI. + */ + public static String getPathLabel(File file) { + return markLTR(file.getAbsolutePath()); + } + + /** + * Returns the label for a file pattern like '*.java' + * + * @param name + * the pattern + * @return the label of the pattern. + */ + public static String getFilePattern(String name) { + return markLTR(name, FILE_PATTERN_DELIMITERS); + } + + /** + * Returns the label for a URL, URI or URL part. Example is + * 'http://www.x.xom/s.html#1' + * + * @param name + * the URL string + * @return the label of the URL. + */ + public static String getURLPart(String name) { + return markLTR(name, URL_DELIMITERS); + } + + /** + * Returns a label for a resource name. + * + * @param resource + * the resource + * @return the label of the resource name. + */ + public static String getResourceName(IResource resource) { + return markLTR(resource.getName()); + } + + /** + * Returns a label for a resource name. + * + * @param resourceName + * the resource name + * @return the label of the resource name. + */ + public static String getResourceName(String resourceName) { + return markLTR(resourceName); + } + + /** + * Returns a label for Java code snippet used in a label. Example is 'Test + * test= new Test() { ...}'. + * + * @param string + * the Java code snippet + * @return the label for the Java code snippet + */ + public static String getJavaCodeString(String string) { + return markLTR(string, CODE_DELIMITERS); + } + + /** + * Returns a label for a version name. Example is '1.4.1' + * + * @param name + * the version string + * @return the version label + */ + public static String getVersionName(String name) { + return markLTR(name); + } + + /** + * Returns a label for a working set + * + * @param set + * the working set + * @return the label of the working set + */ + public static String getWorkingSetLabel(IWorkingSet set) { + return markLTR(set.getLabel()); + } + + /** + * It does not do anything now, but just in case we need to do the same as + * JDT does (see String.markLTR in JDT) + */ + private static String markLTR(String label) { + return label; + } + + private static String markLTR(String label, String delim) { + return label; + } +} diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemComposite.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/CustomizeProblemComposite.java similarity index 79% rename from codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemComposite.java rename to codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/CustomizeProblemComposite.java index 9ec2d404b9b..996d6bdbf45 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/CustomizeProblemComposite.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/CustomizeProblemComposite.java @@ -8,16 +8,16 @@ * Contributors: * Alena Laskavaia - initial API and implementation *******************************************************************************/ -package org.eclipse.cdt.codan.internal.ui.dialogs; +package org.eclipse.cdt.codan.internal.ui.widgets; import org.eclipse.cdt.codan.core.model.IProblem; import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; +import org.eclipse.core.resources.IResource; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; @@ -29,17 +29,21 @@ public class CustomizeProblemComposite extends Composite { private Composite parametersTab; private IProblem problem; private ParametersComposite problemsComposite; + private FileScopeComposite scopeComposite; + private IResource resource; /** * @param parent * @param selectedProblem + * @param resource * @param style */ - public CustomizeProblemComposite(Composite parent, IProblem selectedProblem) { + public CustomizeProblemComposite(Composite parent, + IProblem selectedProblem, IResource resource) { super(parent, SWT.NONE); - this.setLayout(new GridLayout(1, false)); this.problem = selectedProblem; + this.resource = resource; final TabFolder tabFolder = new TabFolder(this, SWT.TOP); tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH)); // createMainTab(tabFolder); @@ -49,6 +53,7 @@ public class CustomizeProblemComposite extends Composite { public void save(IProblemWorkingCopy problem) { problemsComposite.save(problem); + scopeComposite.save(problem); } /** @@ -61,7 +66,8 @@ public class CustomizeProblemComposite extends Composite { tabItem1.setControl(parametersTab); parametersTab.setLayout(new GridLayout()); problemsComposite = new ParametersComposite(parametersTab, problem); - problemsComposite.setLayoutData(new GridData(SWT.BEGINNING,SWT.BEGINNING, true, false)); + problemsComposite.setLayoutData(new GridData(SWT.BEGINNING, + SWT.BEGINNING, true, false)); } /** @@ -73,8 +79,8 @@ public class CustomizeProblemComposite extends Composite { Composite comp = new Composite(tabFolder, SWT.NONE); tabItem1.setControl(comp); comp.setLayout(new GridLayout()); - Label label = new Label(comp, SWT.NONE); - label.setText("Scope: TODO"); //$NON-NLS-1$ - label.setLayoutData(new GridData(GridData.FILL_BOTH)); + scopeComposite = new FileScopeComposite(comp, problem, resource); + scopeComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, + true, false)); } } diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/FileScopeComposite.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/FileScopeComposite.java new file mode 100644 index 00000000000..37e75f497b4 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/FileScopeComposite.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2009 Alena Laskavaia + * 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: + * Alena Laskavaia - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.ui.widgets; + +import org.eclipse.cdt.codan.core.model.IProblem; +import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; +import org.eclipse.cdt.codan.core.param.IProblemPreference; +import org.eclipse.cdt.codan.core.param.MapProblemPreference; +import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; +import org.eclipse.cdt.codan.internal.ui.preferences.FileScopePreferencePage; +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.preference.PreferenceStore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +/** + * Composite to show problem scope + * + */ +public class FileScopeComposite extends Composite { + private FileScopePreferencePage page; + private IProblem problem; + private PreferenceStore prefStore; + private FileScopeProblemPreference scope; + + /** + * @param parent + * @param problem + * @param resource + * @param style + */ + public FileScopeComposite(Composite parent, final IProblem problem, + IResource resource) { + super(parent, SWT.NONE); + if (problem == null) + throw new NullPointerException(); + this.setLayout(new GridLayout(2, false)); + this.problem = problem; + this.prefStore = new PreferenceStore(); + IProblemPreference info = problem.getPreference(); + FileScopeProblemPreference scopeIn = null; + if (info == null + || (!(info instanceof MapProblemPreference)) + || ((scopeIn = (FileScopeProblemPreference) ((MapProblemPreference) info) + .getChildDescriptor(FileScopeProblemPreference.KEY)) == null)) { + Label label = new Label(this, 0); + label.setText(CodanUIMessages.ParametersComposite_None); + return; + } + scope = (FileScopeProblemPreference) scopeIn.clone(); + scope.setResource(resource); + initPrefStore(); + page = new FileScopePreferencePage(scope); + page.setPreferenceStore(prefStore); + page.noDefaultAndApplyButton(); + page.createControl(parent); + page.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); + } + + public void save(IProblemWorkingCopy problem) { + if (page != null) + page.performOk(); + savePrefStore(); + } + + private void savePrefStore() { + if (scope == null) + return; + String key = scope.getQualifiedKey(); + ((MapProblemPreference) problem.getPreference()).setChildValue( + FileScopeProblemPreference.KEY, scope); + prefStore.setValue(key, scope.exportValue()); + } + + private void initPrefStore() { + if (scope == null) + return; + String key = scope.getQualifiedKey(); + prefStore.setValue(key, scope.exportValue()); + } + + /** + * @return the problem + */ + public IProblem getProblem() { + return problem; + } +} diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ParametersComposite.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/ParametersComposite.java similarity index 87% rename from codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ParametersComposite.java rename to codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/ParametersComposite.java index 25100c8d3e4..592b228c6e2 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/dialogs/ParametersComposite.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/widgets/ParametersComposite.java @@ -8,12 +8,13 @@ * Contributors: * Alena Laskavaia - initial API and implementation *******************************************************************************/ -package org.eclipse.cdt.codan.internal.ui.dialogs; +package org.eclipse.cdt.codan.internal.ui.widgets; import java.io.File; import org.eclipse.cdt.codan.core.model.IProblem; import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.codan.core.param.FileScopeProblemPreference; import org.eclipse.cdt.codan.core.param.IProblemPreference; import org.eclipse.cdt.codan.core.param.IProblemPreferenceCompositeDescriptor; import org.eclipse.cdt.codan.core.param.ListProblemPreference; @@ -21,6 +22,7 @@ import org.eclipse.cdt.codan.internal.ui.CodanUIMessages; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.FileFieldEditor; import org.eclipse.jface.preference.ListEditor; import org.eclipse.jface.preference.PreferenceStore; import org.eclipse.jface.preference.StringFieldEditor; @@ -67,6 +69,8 @@ public class ParametersComposite extends Composite { final IProblemPreference info) { if (info == null) return; + if (info.getKey() == FileScopeProblemPreference.KEY) + return; // skip the scope switch (info.getType()) { case TYPE_STRING: { StringFieldEditor fe = new StringFieldEditor( @@ -103,11 +107,12 @@ public class ParametersComposite extends Composite { @Override protected String getNewInputObject() { ListProblemPreference list = (ListProblemPreference) info; - String label = list - .getChildDescriptor() + String label = list.getChildDescriptor() .getLabel(); InputDialog dialog = new InputDialog( - getShell(), CodanUIMessages.ParametersComposite_NewValue, label, "", null); //$NON-NLS-1$ + getShell(), + CodanUIMessages.ParametersComposite_NewValue, + label, "", null); //$NON-NLS-1$ if (dialog.open() == Window.OK) { return dialog.getValue(); } @@ -136,6 +141,20 @@ public class ParametersComposite extends Composite { createFieldEditorsForParameters(desc); } break; + case TYPE_CUSTOM: { + StringFieldEditor fe = new StringFieldEditor( + info.getQualifiedKey(), info.getLabel(), + getFieldEditorParent()); + addField(fe); + break; + } + case TYPE_FILE: { + FileFieldEditor fe = new FileFieldEditor( + info.getQualifiedKey(), info.getLabel(), + getFieldEditorParent()); + addField(fe); + break; + } default: throw new UnsupportedOperationException(info.getType() .toString()); @@ -179,6 +198,9 @@ public class ParametersComposite extends Composite { case TYPE_LIST: desc.importValue(prefStore.getString(key)); break; + case TYPE_CUSTOM: + desc.importValue(prefStore.getString(key)); + break; case TYPE_MAP: IProblemPreference[] childrenDescriptor = ((IProblemPreferenceCompositeDescriptor) desc) .getChildDescriptors(); @@ -213,6 +235,9 @@ public class ParametersComposite extends Composite { case TYPE_LIST: prefStore.setValue(key, desc.exportValue()); break; + case TYPE_CUSTOM: + prefStore.setValue(key, desc.exportValue()); + break; case TYPE_MAP: IProblemPreference[] childrenDescriptor = ((IProblemPreferenceCompositeDescriptor) desc) .getChildDescriptors();