Gridarta Editor
AttributeBitmask.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.model.archetypetype;
21 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
30 import org.apache.log4j.Category;
31 import org.apache.log4j.Logger;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34 
42 public class AttributeBitmask {
43 
47  @NotNull
48  private static final Category LOG = Logger.getLogger(AttributeBitmask.class);
49 
54  private static final int MAX_CHARS_PER_LINE = 35; // 50
55 
59  @NotNull
60  private final List<String> bitName = new ArrayList<>();
61 
66  private final boolean isNamed;
67 
71  @NotNull
72  private final Map<String, Integer> namedValues = new HashMap<>();
73 
77  @NotNull
78  private final Map<Integer, String> encodings = new HashMap<>();
79 
83  @NotNull
84  private final Map<Integer, String> names = new HashMap<>();
85 
91  public AttributeBitmask(final boolean isNamed) {
92  this.isNamed = isNamed;
93  }
94 
100  @NotNull
101  // feature envy is natural here - WrappingStringBuilder is a kind of library class.
102  public String getText(final int value) {
103  final WrappingStringBuilder sb = new WrappingStringBuilder(MAX_CHARS_PER_LINE);
104  for (final String word : encodeValueAsList(value, names)) {
105  sb.append(word);
106  }
107  return sb.toString();
108  }
109 
119  @NotNull
120  public String encodeValue(final int value) {
121  if (!isNamed) {
122  return Integer.toString(value);
123  }
124 
125  final StringBuilder sb = new StringBuilder();
126  for (final String word : encodeValueAsList(value, encodings)) {
127  if (sb.length() > 0) {
128  sb.append(' ');
129  }
130  sb.append(word);
131  }
132  return sb.toString();
133  }
134 
145  @NotNull
146  private Iterable<String> encodeValueAsList(final int value, @NotNull final Map<Integer, String> strings) {
147  final Collection<String> result = new ArrayList<>();
148 
149  final int moveAll = getMaxValue();
150 
151  final Collection<String> negResult = new ArrayList<>();
152  negResult.add("all");
153 
154  if (value == moveAll) {
155  return negResult;
156  }
157  if (value == 0) {
158  final String str = strings.get(0);
159  result.add(str == null ? "0" : str);
160  return result;
161  }
162 
163  /* We basically slide the bits down. Why look at moveAll? because we
164  * may want to return a string like 'all -swim', and if we just looked
165  * at mt, we couldn't get that.
166  */
167  int allCount = 0;
168  int count = 0;
169  for (int i = moveAll; i != 0; i >>= 1) {
170  final String strNull = strings.get(1 << count);
171  final String str = strNull == null ? Integer.toString(1 << count) : strNull;
172  if ((value & (1 << count)) == 0) {
173  negResult.add("-" + str);
174  allCount++;
175  } else {
176  result.add(str);
177  }
178  count++;
179  }
180  if ((value & ~moveAll) != 0) {
181  result.add(Integer.toString(value & ~moveAll));
182  return result;
183  }
184  /* Basically, if there is a single negation, return it, eg 'all -swim'.
185  * But more than that, just return the enumerated values. It doesn't
186  * make sense to return 'all -walk -fly_low' - it is shorter to return
187  * 'fly_high swim'
188  */
189  return count >= 4 && allCount <= 1 ? negResult : result;
190  }
191 
197  public int decodeValue(@NotNull final String encodedValue) {
198  if (!isNamed) {
199  return NumberUtils.parseInt(encodedValue);
200  }
201 
202  if (encodedValue.isEmpty()) {
203  return 0;
204  }
205 
206  int result = 0;
207  for (final String word : StringUtils.PATTERN_SPACES.split(encodedValue, 0)) {
208  final boolean negated;
209  final String name;
210  if (word.startsWith("-")) {
211  negated = true;
212  name = word.substring(1);
213  } else {
214  negated = false;
215  name = word;
216  }
217  final Integer integerValue = namedValues.get(name);
218  int value;
219  if (integerValue != null) {
220  value = integerValue;
221  } else {
222  try {
223  value = Integer.parseInt(name);
224  } catch (final NumberFormatException ignored) {
225  LOG.warn("Ignoring unknown bitmask value: " + name);
226  value = 0;
227  }
228  }
229  if (negated) {
230  result &= ~value;
231  } else {
232  result |= value;
233  }
234  }
235  return result;
236  }
237 
242  public int getMaxValue() {
243  return (1 << bitName.size()) - 1;
244  }
245 
250  public int getNumber() {
251  return bitName.size();
252  }
253 
259  @Nullable
260  public String getBitName(final int index) {
261  return bitName.get(index);
262  }
263 
269  public void addName(@NotNull final String name, final int value) {
270  names.put(value, name);
271  }
272 
278  public void addNamedValue(@NotNull final String name, final int value) {
279  namedValues.put(name, value);
280  encodings.put(value, name);
281  }
282 
288  public boolean containsEncoding(final int value) {
289  return encodings.containsKey(value);
290  }
291 
298  public boolean addBitName(final int bitValue, @NotNull final String name) {
299  if (bitValue >= bitName.size()) {
300  do {
301  bitName.add(null);
302  } while (bitValue >= bitName.size());
303  } else if (bitName.get(bitValue) != null) {
304  return false;
305  }
306 
307  bitName.set(bitValue, name);
308  return true;
309  }
310 
311 }
Utility class for string manipulation.
static final int MAX_CHARS_PER_LINE
Maximum number of characters in a line before line break (see getText(int)).
boolean addBitName(final int bitValue, @NotNull final String name)
Defines a bit name for a bit value.
String getBitName(final int index)
Returns the name of a bitmask value.
Utility class for parsing strings into numbers.
static final Pattern PATTERN_SPACES
The pattern that matches a non-empty sequence of spaces.
final List< String > bitName
The names of the bitmask entries.
int getMaxValue()
Returns the maximum allowed bitmask value.
Base package of all Gridarta classes.
void addName(@NotNull final String name, final int value)
Adds a readable name for a bit value.
String encodeValue(final int value)
Convert a value to external representation.
void append(final String str)
Append a word.
int getNumber()
Returns the number of bitmask entries (not counting zero).
AttributeBitmask(final boolean isNamed)
Constructor of a bitmask from XML element.
static int parseInt(@NotNull final String s)
Parses an integer string.
Iterable< String > encodeValueAsList(final int value, @NotNull final Map< Integer, String > strings)
Convert a value to string representation.
This class manages bitmask values which appear in Gridarta archetype attributes.
final Map< String, Integer > namedValues
Maps names for value encoding in external representation.
int decodeValue(@NotNull final String encodedValue)
Convert a value from external representation.
final boolean isNamed
Set if the bitmask value may be encoded as strings in external representation; unset if the value is ...
void addNamedValue(@NotNull final String name, final int value)
Adds a name for external encoding of a value.
String getText(final int value)
Generate the text to be displayed for a given bitmask value.
static final Category LOG
The Logger for printing log messages.
String toString()
Return the concatenated words as a string.
final Map< Integer, String > encodings
Maps bit value to external representation.
final Map< Integer, String > names
Maps bit value to readable name.
boolean containsEncoding(final int value)
Returns whether an external encoding for value exists.
Implements a string buffer that separates words by "," and wraps lines at a given margin...