View Javadoc
1   /*
2    * Copyright (C) 2019 sw4j.org
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package org.sw4j.tool.barcode.random.generator;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Random;
25  import java.util.Set;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  import org.sw4j.tool.barcode.random.config.EncodingConfig;
29  import org.sw4j.tool.barcode.random.encoder.ByteArrayEncoder;
30  
31  /**
32   * <p>
33   * This class contains the random number and the encoded representations for a single ident.
34   * </p>
35   * <p>
36   * This class is immutable.
37   * </p>
38   * @author Uwe Plonus &lt;u.plonus@gmail.com&gt;
39   */
40  public class RandomIdent implements IdentValue {
41  
42      /**
43       * <p>
44       * The ident number.
45       * </p>
46       */
47      private final String ident;
48  
49      /**
50       * <p>
51       * The generated random number.
52       * </p>
53       */
54      private final byte[] random;
55  
56      /**
57       * <p>
58       * A map containing all encoded values for the ident. The key is the encoding and the value the encoded value.
59       * </p>
60       */
61      private final Map<EncodingConfig, String> encoded;
62  
63      /**
64       * <p>
65       * Create a new {@code RandomIdent} and the random number for the {@code ident}. The random number has a size of
66       * {@code randomSize} bits (rounded down to the next whole byte). For the generation of the random number the given
67       * {@code rng} will be used. The generated random number will be encoded in all given {@code encodings}.
68       * </p>
69       * <p>
70       * For high quality random numbers use an appropriate random number generator
71       * (e.g. {@link java.security.SecureRandom SecureRandom}).
72       * </p>
73       * @param ident the ident for which the random number will be generated for.
74       * @param randomSize the size in bits (rounded down to the next byte).
75       * @param rng the random number generator to use.
76       * @param encodings the encodings that are used for the encoded representation of the random number.
77       * @throws IllegalArgumentException if no encoder for a given encoding can be found.
78       */
79      public RandomIdent(final String ident, final int randomSize, final Random rng,
80              final Set<EncodingConfig> encodings) {
81          this.ident = ident;
82          random = new byte[randomSize / 8];
83          rng.nextBytes(random);
84          encoded = new HashMap<>();
85          encodings.forEach((encoding) -> {
86              String rawEncoding = encoding.getType();
87              int endIndex = -1;
88              int startIndex = -1;
89              boolean hasMinus = false;
90              if (rawEncoding.matches(".+\\{\\d*-?\\d+\\}")) {
91                  Pattern p = Pattern.compile("(.+)\\{((\\d*)-)?(\\d+)\\}");
92                  Matcher m = p.matcher(rawEncoding);
93                  m.matches();
94                  rawEncoding = m.group(1);
95                  if (m.group(2) != null && m.group(2).length() > 1) {
96                      startIndex = Integer.parseInt(m.group(2).substring(0, m.group(2).length() - 1));
97                  }
98                  String startIndexGroup = m.group(2);
99                  hasMinus = m.group(3) != null;
100                 endIndex = Integer.parseInt(m.group(4));
101             }
102             ByteArrayEncoder encoder = ByteArrayEncoder.forEncoding(rawEncoding);
103             if (encoder == null) {
104                 throw new IllegalArgumentException(
105                         String.format("Cannot find an encoder for encoding %s", rawEncoding));
106             }
107             String encodedValue = encoder.encode(random);
108             if (endIndex > 0) {
109                 if (hasMinus) {
110                     if (startIndex >= 0) {
111                         encodedValue = encodedValue.substring(startIndex, endIndex);
112                     } else {
113                         encodedValue = encodedValue.substring(encodedValue.length() - endIndex);
114                     }
115                 } else {
116                     encodedValue = encodedValue.substring(0, endIndex);
117                 }
118             }
119             if (encoding.getSepCount() != null) {
120                 int partsCount = encodedValue.length() / encoding.getSepCount();
121                 if (encodedValue.length() % encoding.getSepCount() != 0) {
122                     partsCount++;
123                 }
124                 List<String> parts = new ArrayList<>(partsCount);
125                 for (int i = 0; i < partsCount; i++) {
126                     int endPos = (i + 1) * encoding.getSepCount() < encodedValue.length() ?
127                             (i + 1) * encoding.getSepCount() :
128                             encodedValue.length();
129                     parts.add(encodedValue.substring(i * encoding.getSepCount(),
130                             endPos));
131                 }
132                 StringBuilder sb = new StringBuilder();
133                 for (int i = 0; i < parts.size(); i++) {
134                     if (i > 0) {
135                         sb.append(encoding.getSepChar());
136                     }
137                     sb.append(parts.get(i));
138                 }
139                 encodedValue = sb.toString();
140             }
141             encoded.put(encoding, encodedValue);
142         });
143     }
144 
145     /**
146      * <p>
147      * Return the ident number.
148      * </p>
149      * @return the ident number.
150      */
151     @Override
152     public String getIdent() {
153         return ident;
154     }
155 
156     /**
157      * <p>
158      * Return the raw random number generated.
159      * </p>
160      * @return the raw random number.
161      */
162     @Override
163     public byte[] getValue() {
164         return Arrays.copyOf(random, random.length);
165     }
166 
167     /**
168      * <p>
169      * Return the random number in the given {@code encoding}. Only the encodings that were given during construction
170      * can be returned.
171      * </p>
172      * @param encoding the encoding for which the encoded value should be returned.
173      * @return the encoded value or {@code null} if the encoding is unknown.
174      */
175     @Override
176     public String getEncoded(final EncodingConfig encoding) {
177         return encoded.get(encoding);
178     }
179 
180 }