001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.joran.spi;
015
016import java.util.List;
017
018/**
019 * ElementSelector extends {@link ElementPath} with matching operations such as
020 * {@link #fullPathMatch(ElementPath)},
021 * {@link #getPrefixMatchLength(ElementPath)} and
022 * {@link #getTailMatchLength(ElementPath)}.
023 *
024 * <p>
025 * Parts of the path may contain '*' for wildcard matching.
026 *
027 * @author Ceki G&uuml;lc&uuml;
028 * @since 1.1.0
029 */
030public class ElementSelector extends ElementPath {
031
032    public ElementSelector() {
033        super();
034    }
035
036    public ElementSelector(List<String> list) {
037        super(list);
038    }
039
040    /**
041     * Build an elementPath from a string.
042     *
043     * Note that "/x" is considered equivalent to "x" and to "x/"
044     *
045     */
046    public ElementSelector(String p) {
047        super(p);
048    }
049
050    public boolean fullPathMatch(ElementPath path) {
051        if (path.size() != size()) {
052            return false;
053        }
054
055        int len = size();
056        for (int i = 0; i < len; i++) {
057            if (!equalityCheck(get(i), path.get(i))) {
058                return false;
059            }
060        }
061        // if everything matches, then the two patterns are equal
062        return true;
063    }
064
065    /**
066     * Returns the number of "tail" components that this pattern has in common with
067     * the pattern p passed as parameter. By "tail" components we mean the
068     * components at the end of the pattern.
069     */
070    public int getTailMatchLength(ElementPath p) {
071        if (p == null) {
072            return 0;
073        }
074
075        int lSize = this.partList.size();
076        int rSize = p.partList.size();
077
078        // no match possible for empty sets
079        if ((lSize == 0) || (rSize == 0)) {
080            return 0;
081        }
082
083        int minLen = (lSize <= rSize) ? lSize : rSize;
084        int match = 0;
085
086        // loop from the end to the front
087        for (int i = 1; i <= minLen; i++) {
088            String l = this.partList.get(lSize - i);
089            String r = p.partList.get(rSize - i);
090
091            if (equalityCheck(l, r)) {
092                match++;
093            } else {
094                break;
095            }
096        }
097        return match;
098    }
099
100    public boolean isContainedIn(ElementPath p) {
101        if (p == null) {
102            return false;
103        }
104        return p.toStableString().contains(toStableString());
105    }
106
107    /**
108     * Returns the number of "prefix" components that this pattern has in common
109     * with the pattern p passed as parameter. By "prefix" components we mean the
110     * components at the beginning of the pattern.
111     */
112    public int getPrefixMatchLength(ElementPath p) {
113        if (p == null) {
114            return 0;
115        }
116
117        int lSize = this.partList.size();
118        int rSize = p.partList.size();
119
120        // no match possible for empty sets
121        if ((lSize == 0) || (rSize == 0)) {
122            return 0;
123        }
124
125        int minLen = (lSize <= rSize) ? lSize : rSize;
126        int match = 0;
127
128        for (int i = 0; i < minLen; i++) {
129            String l = this.partList.get(i);
130            String r = p.partList.get(i);
131
132            if (equalityCheck(l, r)) {
133                match++;
134            } else {
135                break;
136            }
137        }
138
139        return match;
140    }
141
142    private boolean equalityCheck(String x, String y) {
143        return x.equalsIgnoreCase(y);
144    }
145
146    @Override
147    public boolean equals(Object o) {
148        if ((o == null) || !(o instanceof ElementSelector)) {
149            return false;
150        }
151
152        ElementSelector r = (ElementSelector) o;
153
154        if (r.size() != size()) {
155            return false;
156        }
157
158        int len = size();
159
160        for (int i = 0; i < len; i++) {
161            if (!equalityCheck(get(i), r.get(i))) {
162                return false;
163            }
164        }
165
166        // if everything matches, then the two patterns are equal
167        return true;
168    }
169
170    @Override
171    public int hashCode() {
172        int hc = 0;
173        int len = size();
174
175        for (int i = 0; i < len; i++) {
176            // make Pattern comparisons case insensitive
177            // http://jira.qos.ch/browse/LBCORE-76
178            hc ^= get(i).toLowerCase().hashCode();
179        }
180        return hc;
181    }
182
183}