/******************************************************************************* * Copyright (c) 2015 ARM Ltd. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * ARM Ltd and ARM Germany GmbH - Initial API and implementation *******************************************************************************/ package com.arm.cmsis.pack.utils; /** * Utility class provides method to compare two string that can contain wild cards * * Supported wild cards expressions: * <dl> * <dt>*<dd>any substring * <dt>?<dd>any single character * <dt>[abc]<dd>any character in a set * </dl> * * Both stings can be wild card patterns, for example "a*d" and "a*" * * The main purpose of this class is to support condition evaluation in CMSIS packs. * It is optimized for small strings, primary for device names like <b>"STM32F4[23]9??</b>". * <p/> * The method has some limitations: * <ul> * <li>there is no escape for *, ? [ and ] characters * <li>"*?" is equivalent to "*" => <code>"a*?d"</code> and <code>"a*d"</code> are equivalent * <li>* match is performed until first matching on character followed after * is found, therefore * pattern <code>"a*d"</code> will match <code>"a.d"</code> or <code>"a.c.d"</code>, but not <code>"a.d.d"</code>" * </ul> */ public class WildCards { /** * Match two strings containing wild cards (case sensitive) * @param str1 first string argument * @param str2 second string argument * @return <b>true</b> if strings match, <b>false</b> otherwise */ public static boolean match(final String str1, final String str2) { return match(str1, str2, true); } /** * Match two strings containing wild cards ignoring case * @param str1 first string argument * @param str2 second string argument * @return <b>true</b> if strings match, <b>false</b> otherwise */ public static boolean matchNoCase(final String str1, final String str2) { return match(str1, str2, false); } /** * Match two strings containing wild cards * @param str1 first string argument * @param str2 second string argument * @param cs case sensitive flag (true: respect case, false: ignore case) * @return <b>true</b> if strings match, <b>false</b> otherwise */ public static boolean match(final String str1, final String str2, boolean cs) { // check for empty and null strings if (str1 == null || str1.isEmpty()) { if (str2 == null || str2.isEmpty()) return true; return false; } else if (str2 == null || str2.isEmpty()) { return false; } WildcardState ws1 = new WildcardState(str1, cs); if (ws1.isAsterisk() && ws1.isEnd()) return true; WildcardState ws2 = new WildcardState(str2, cs); if (ws2.isAsterisk() && ws2.isEnd()) return true; boolean result = wildCardMatch(ws1, ws2); // we need a symmetric comparison in case both strings contain '*' : // a*d and a*cd should be treated as equal if (!result & ws1.containsAsterisk() && ws2.containsAsterisk()) { ws1.init(); ws2.init(); if (wildCardMatch(ws2, ws1)) return true; } return result; } private static boolean wildCardMatch(WildcardState ws1, WildcardState ws2) { while (true) { if (ws1.isAsterisk()) { if (ws1.isEnd()) return true; // end of str2 is irrelevant ws2.skip(ws1); if (ws2.isEnd()) { return ws2.isAsterisk() || ws2.isQuestion(); } } if (ws2.isAsterisk()) { if (ws2.isEnd()) return true; // end of str1 is irrelevant ws1.skip(ws2); if (ws1.isEnd()) { return ws1.isAsterisk() || ws1.isQuestion(); } } if (ws1.isEnd() || ws2.isEnd()) break; if (!ws1.compare(ws2)) { return false; } ws1.next(); ws2.next(); } return ws1.isEnd() && ws2.isEnd(); } static private class WildcardState { private String s; private boolean cs = true; private int index = 0; private int rangeFrom = -1; private int rangeTo = -1; private boolean asterisk = false; private boolean containsAsterisk = false; public WildcardState(String s, boolean cs ) { this.s = s; this.cs = cs; init(); } void init() { asterisk = false; containsAsterisk = false; index = 0; createRange(); } public boolean containsAsterisk() { return containsAsterisk; } boolean isEnd() { return index >= s.length(); } boolean isAsterisk() { return asterisk; } boolean isQuestion() { return rangeFrom >=0 && s.charAt(rangeFrom) == '?'; } void next() { if (isEnd()) return; index++; createRange(); } void skip(WildcardState ws) { while (!isEnd() && !compare(ws)) { next(); } } boolean compare(WildcardState ws) { if (isQuestion() || ws.isQuestion()) return true; if(rangeFrom < 0 && ws.rangeFrom < 0) return true; for(int i = rangeFrom; i < rangeTo; i++) { char ch = s.charAt(i); if(!cs) ch = Character.toUpperCase(ch); for(int j = ws.rangeFrom; j < ws.rangeTo; j++) { char otherCh = ws.s.charAt(j); if(!cs) otherCh = Character.toUpperCase(otherCh); if(ch == otherCh) return true; } } return false; } void createRange() { rangeFrom = rangeTo = -1; asterisk = false; if (isEnd()) return; char ch = s.charAt(index); if (ch == '*') { containsAsterisk = asterisk = true; // skip all asterisks and questions index++; while (!isEnd()) { ch = s.charAt(index); if(ch != '*' && ch != '?') break; } } if (isEnd()) return; if (ch == '[') { index++; if (isEnd()) return; rangeTo = rangeFrom = index; while (!isEnd()) { ch = s.charAt(index); if (ch == ']') break; index++; rangeTo = index; } } else { rangeTo = rangeFrom = index; rangeTo++; } } } }