Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MaskedBinaryExpression |
|
| 10.0;10 |
1 | /* | |
2 | * Copyright (c) 2010 - 2011 Michael Schierl | |
3 | * | |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * - Redistributions of source code must retain the above copyright notice, | |
11 | * this list of conditions and the following disclaimer. | |
12 | * | |
13 | * - Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * | |
17 | * - Neither name of the copyright holders nor the names of its | |
18 | * contributors may be used to endorse or promote products derived from | |
19 | * this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | |
28 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
29 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
30 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
31 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | package net.sf.jmatchparser.util; | |
34 | ||
35 | import java.io.IOException; | |
36 | import java.util.Arrays; | |
37 | import java.util.regex.Pattern; | |
38 | ||
39 | /** | |
40 | * A {@link BinaryExpression} that uses the secondary name "mask" in non-random | |
41 | * mode to specify a bit mask that is ANDed with the expression to test. | |
42 | * Optional bytes at the end of the mask mean that the string to match may also | |
43 | * be shorter. Optional bytes at other positions are invalid. | |
44 | * | |
45 | * <p> | |
46 | * Alternatively, this class can match based on a regular expression, by using | |
47 | * <tt>regex:<i>someRegex</i></tt> as the syntax. No additional transforms are | |
48 | * parsed in this case. | |
49 | */ | |
50 | public class MaskedBinaryExpression { | |
51 | ||
52 | private final Pattern regex; | |
53 | private final byte[] bytes, mask; | |
54 | private final int minLength; | |
55 | ||
56 | /** | |
57 | * Create a new masked binary expression. | |
58 | */ | |
59 | 19 | public MaskedBinaryExpression(String expression) throws IOException { |
60 | 19 | if (expression.startsWith("regex:")) { |
61 | 4 | regex = Pattern.compile(expression.substring(6)); |
62 | 4 | minLength = -1; |
63 | 4 | bytes = mask = null; |
64 | } else { | |
65 | 15 | BinaryExpression binex = new BinaryExpression(expression, "mask", false); |
66 | 15 | bytes = binex.getValue(); |
67 | 15 | byte[] maskTemp = binex.getSecondaryValue(); |
68 | 15 | if (maskTemp == null) { |
69 | 3 | mask = new byte[bytes.length]; |
70 | 3 | Arrays.fill(mask, (byte) 0xff); |
71 | 12 | } else if (maskTemp.length < bytes.length) { |
72 | 3 | mask = new byte[bytes.length]; |
73 | 3 | System.arraycopy(maskTemp, 0, mask, 0, maskTemp.length); |
74 | 3 | Arrays.fill(mask, maskTemp.length, mask.length, (byte) 0xff); |
75 | 9 | } else if (maskTemp.length > bytes.length) { |
76 | 1 | throw new IOException("Mask length " + maskTemp.length + " is larger than value length " + bytes.length); |
77 | } else { | |
78 | 8 | mask = maskTemp; |
79 | } | |
80 | 14 | boolean[] markedBytes = binex.getMarkedSecondaryBytes(); |
81 | 14 | int minLengthTemp = bytes.length; |
82 | 14 | if (markedBytes != null) { |
83 | 11 | minLengthTemp = markedBytes.length; |
84 | 50 | for (int i = 0; i < markedBytes.length; i++) { |
85 | 40 | if (markedBytes[i]) { |
86 | 18 | minLengthTemp = Math.min(minLengthTemp, i); |
87 | 22 | } else if (minLengthTemp != markedBytes.length) { |
88 | 1 | throw new IOException("Mandatory bytes after optional bytes are not supported!"); |
89 | } | |
90 | } | |
91 | } | |
92 | 13 | minLength = minLengthTemp; |
93 | 13 | regex = null; |
94 | } | |
95 | 17 | } |
96 | ||
97 | /** | |
98 | * Test whether this expression matches the given bytes. | |
99 | */ | |
100 | public boolean matches(byte[] value) throws IOException { | |
101 | 17 | if (regex != null) { |
102 | 4 | return regex.matcher(new String(value, "ISO-8859-1")).matches(); |
103 | } | |
104 | 13 | if (value.length > bytes.length || value.length < minLength) |
105 | 5 | return false; |
106 | 28 | for (int i = 0; i < value.length; i++) { |
107 | 22 | if ((value[i] & mask[i]) != (bytes[i] & mask[i])) |
108 | 2 | return false; |
109 | } | |
110 | 6 | return true; |
111 | } | |
112 | } |