1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.matcher;
17
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20
21 /**
22 * An element matcher that compares two strings by a given pattern which is characterized by a
23 * {@link net.bytebuddy.matcher.StringMatcher.Mode}.
24 */
25 @HashCodeAndEqualsPlugin.Enhance
26 public class StringMatcher extends ElementMatcher.Junction.AbstractBase<String> {
27
28 /**
29 * The text value to match against.
30 */
31 private final String value;
32
33 /**
34 * The mode to apply for matching the given value against the matcher's input.
35 */
36 private final Mode mode;
37
38 /**
39 * Creates a new string matcher.
40 *
41 * @param value The value that is the base of the matching.
42 * @param mode The mode to apply for matching the given value against the matcher's input
43 */
44 public StringMatcher(String value, Mode mode) {
45 this.value = value;
46 this.mode = mode;
47 }
48
49 /**
50 * {@inheritDoc}
51 */
52 public boolean matches(String target) {
53 return mode.matches(value, target);
54 }
55
56 @Override
57 public String toString() {
58 return mode.getDescription() + '(' + value + ')';
59 }
60
61 /**
62 * Defines the mode a {@link net.bytebuddy.matcher.StringMatcher} compares to strings with.
63 */
64 public enum Mode {
65
66 /**
67 * Checks if two strings equal and respects casing differences.
68 */
69 EQUALS_FULLY("equals") {
70 @Override
71 protected boolean matches(String expected, String actual) {
72 return actual.equals(expected);
73 }
74 },
75
76 /**
77 * Checks if two strings equal without respecting casing differences.
78 */
79 EQUALS_FULLY_IGNORE_CASE("equalsIgnoreCase") {
80 @Override
81 protected boolean matches(String expected, String actual) {
82 return actual.equalsIgnoreCase(expected);
83 }
84 },
85
86 /**
87 * Checks if a string starts with the a second string with respecting casing differences.
88 */
89 STARTS_WITH("startsWith") {
90 @Override
91 protected boolean matches(String expected, String actual) {
92 return actual.startsWith(expected);
93 }
94 },
95
96 /**
97 * Checks if a string starts with a second string without respecting casing differences.
98 */
99 STARTS_WITH_IGNORE_CASE("startsWithIgnoreCase") {
100 @Override
101 @SuppressFBWarnings(value = "DM_CONVERT_CASE", justification = "Both strings are transformed by the default locale")
102 protected boolean matches(String expected, String actual) {
103 return actual.toLowerCase().startsWith(expected.toLowerCase());
104 }
105 },
106
107 /**
108 * Checks if a string ends with a second string with respecting casing differences.
109 */
110 ENDS_WITH("endsWith") {
111 @Override
112 protected boolean matches(String expected, String actual) {
113 return actual.endsWith(expected);
114 }
115 },
116
117 /**
118 * Checks if a string ends with a second string without respecting casing differences.
119 */
120 ENDS_WITH_IGNORE_CASE("endsWithIgnoreCase") {
121 @Override
122 @SuppressFBWarnings(value = "DM_CONVERT_CASE", justification = "Both strings are transformed by the default locale")
123 protected boolean matches(String expected, String actual) {
124 return actual.toLowerCase().endsWith(expected.toLowerCase());
125 }
126 },
127
128 /**
129 * Checks if a string contains another string with respecting casing differences.
130 */
131 CONTAINS("contains") {
132 @Override
133 protected boolean matches(String expected, String actual) {
134 return actual.contains(expected);
135 }
136 },
137
138 /**
139 * Checks if a string contains another string without respecting casing differences.
140 */
141 CONTAINS_IGNORE_CASE("containsIgnoreCase") {
142 @Override
143 @SuppressFBWarnings(value = "DM_CONVERT_CASE", justification = "Both strings are transformed by the default locale")
144 protected boolean matches(String expected, String actual) {
145 return actual.toLowerCase().contains(expected.toLowerCase());
146 }
147 },
148
149 /**
150 * Checks if a string can be matched by a regular expression.
151 */
152 MATCHES("matches") {
153 @Override
154 protected boolean matches(String expected, String actual) {
155 return actual.matches(expected);
156 }
157 };
158
159 /**
160 * A description of the string for providing meaningful {@link Object#toString()} implementations for
161 * method matchers that rely on a match mode.
162 */
163 private final String description;
164
165 /**
166 * Creates a new match mode.
167 *
168 * @param description The description of this mode for providing meaningful {@link Object#toString()}
169 * implementations.
170 */
171 Mode(String description) {
172 this.description = description;
173 }
174
175 /**
176 * Returns the description of this match mode.
177 *
178 * @return The description of this match mode.
179 */
180 protected String getDescription() {
181 return description;
182 }
183
184 /**
185 * Matches a string against another string.
186 *
187 * @param expected The target of the comparison against which the {@code actual} string is compared.
188 * @param actual The source which is subject of the comparison to the {@code expected} value.
189 * @return {@code true} if the source matches the target.
190 */
191 protected abstract boolean matches(String expected, String actual);
192 }
193 }
194