Gridarta Editor
BrowseArchetypesTableModel.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.gui.dialog.browsearchetypes;
21 
22 import java.io.BufferedWriter;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.io.OutputStreamWriter;
28 import java.io.Writer;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.WeakHashMap;
33 import java.util.regex.Pattern;
34 import javax.swing.table.AbstractTableModel;
40 import net.sf.japi.swing.action.ActionBuilder;
41 import net.sf.japi.swing.action.ActionBuilderFactory;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44 
50 public class BrowseArchetypesTableModel<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> extends AbstractTableModel {
51 
55  private static final long serialVersionUID = 1L;
56 
60  @NotNull
61  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
62 
67  @NotNull
68  private static final Pattern PATTERN_PLAIN_CSV_VALUE = Pattern.compile("[a-zA-Z_0-9]+");
69 
73  @NotNull
74  private static final Pattern PATTERN_QUOTE = Pattern.compile("\"");
75 
79  @NotNull
81 
85  @NotNull
86  private final List<R> archetypes = new ArrayList<>();
87 
91  @NotNull
92  private final List<String> attributes = new ArrayList<>();
93 
97  @NotNull
98  private final List<Class<?>> classes = new ArrayList<>();
99 
103  @NotNull
104  private final Map<String, Class<?>> classMap = new WeakHashMap<>();
105 
110  public BrowseArchetypesTableModel(@NotNull final ArchetypeSet<G, A, R> archetypeSet) {
111  this.archetypeSet = archetypeSet;
112  update();
113  }
114 
115  @Override
116  public int getRowCount() {
117  return archetypes.size();
118  }
119 
120  @Override
121  public int getColumnCount() {
122  return 2 + attributes.size();
123  }
124 
125  @Nullable
126  @Override
127  public Object getValueAt(final int rowIndex, final int columnIndex) {
128  if (rowIndex < 0 || rowIndex >= archetypes.size()) {
129  return null;
130  }
131  final Archetype<?, ?, ?> archetype = archetypes.get(rowIndex);
132  if (columnIndex == 0) {
133  return archetype.getArchetypeName();
134  } else if (columnIndex == 1) {
135  return archetype.getBestName();
136  } else if (columnIndex >= 0 && columnIndex < 2 + attributes.size()) {
137  final String attribute = attributes.get(columnIndex - 2);
138  final String value = archetype.getAttributeString(attribute);
139  final Class<?> attributeClass = classes.get(columnIndex - 2);
140  if (attributeClass == Long.class) {
141  if (value.isEmpty()) {
142  return null;
143  }
144  try {
145  return Long.valueOf(value);
146  } catch (final NumberFormatException ignored) {
147  }
148  }
149  return value;
150  } else {
151  return null;
152  }
153  }
154 
155  @Override
156  public String getColumnName(final int column) {
157  if (column == 0) {
158  return ActionBuilderUtils.getString(ACTION_BUILDER, "browseArchetypes.table.archetype");
159  } else if (column == 1) {
160  return ActionBuilderUtils.getString(ACTION_BUILDER, "browseArchetypes.table.name");
161  } else if (column >= 0 && column < 2 + attributes.size()) {
162  return attributes.get(column - 2);
163  } else {
164  return super.getColumnName(column);
165  }
166  }
167 
168  @Override
169  public Class<?> getColumnClass(final int columnIndex) {
170  if (columnIndex < 2) {
171  return String.class;
172  } else if (columnIndex >= 0 && columnIndex < 2 + attributes.size()) {
173  return classes.get(columnIndex - 2);
174  } else {
175  return super.getColumnClass(columnIndex);
176  }
177  }
178 
183  private void update() {
184  archetypes.clear();
185  for (final R archetype : archetypeSet.getArchetypes()) {
186  boolean include = false;
187  for (final String attribute : attributes) {
188  if (!archetype.getAttributeString(attribute).isEmpty()) {
189  include = true;
190  break;
191  }
192  }
193  if (include) {
194  archetypes.add(archetype);
195  }
196  }
197  fireTableStructureChanged();
198  }
199 
204  public void addAttribute(@NotNull final String attribute) {
205  classes.add(getAttributeClass(attribute));
206  attributes.add(attribute);
207  update();
208  }
209 
215  @NotNull
216  private Class<?> getAttributeClass(@NotNull final String attribute) {
217  final Class<?> existingClass = classMap.get(attribute);
218  if (existingClass != null) {
219  return existingClass;
220  }
221 
222  for (final Archetype<?, ?, ?> archetype : archetypeSet.getArchetypes()) {
223  final String value = archetype.getAttributeString(attribute);
224  if (value.isEmpty()) {
225  // skip empty values
226  continue;
227  }
228  try {
229  Long.parseLong(value);
230  } catch (final NumberFormatException ignored) {
231  classMap.put(attribute, String.class);
232  return String.class;
233  }
234  }
235  classMap.put(attribute, Long.class);
236  return Long.class;
237  }
238 
244  @Nullable
245  public String removeAttribute(final int index) {
246  final String attribute;
247  try {
248  attribute = attributes.remove(index - 2);
249  classes.remove(index - 2);
250  } catch (final IndexOutOfBoundsException ignored) {
251  return null;
252  }
253  update();
254  return attribute;
255  }
256 
262  @NotNull
263  @SuppressWarnings("TypeMayBeWeakened")
264  public R get(final int index) {
265  return archetypes.get(index);
266  }
267 
273  public void saveAsCsv(@NotNull final File file) throws IOException {
274  try (OutputStream outputStream = new FileOutputStream(file)) {
275  try (Writer writer = new OutputStreamWriter(outputStream)) {
276  try (BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
277  final int columns = getColumnCount();
278  final int rows = getRowCount();
279  for (int column = 0; column < columns; column++) {
280  if (column > 0) {
281  bufferedWriter.write(",");
282  }
283  writeCsvValue(bufferedWriter, getColumnName(column));
284  }
285  bufferedWriter.newLine();
286  for (int row = 0; row < rows; row++) {
287  for (int column = 0; column < columns; column++) {
288  if (column > 0) {
289  bufferedWriter.write(",");
290  }
291  final Object value = getValueAt(row, column);
292  writeCsvValue(bufferedWriter, value == null ? "" : value.toString());
293  }
294  bufferedWriter.newLine();
295  }
296  }
297  }
298  }
299  }
300 
307  private static void writeCsvValue(@NotNull final Writer writer, @NotNull final String value) throws IOException {
308  if (PATTERN_PLAIN_CSV_VALUE.matcher(value).matches()) {
309  writer.write(value);
310  } else {
311  writer.write("\"");
312  writer.write(PATTERN_QUOTE.matcher(value).replaceAll("\"\""));
313  writer.write("\"");
314  }
315  }
316 
317 }
static final Pattern PATTERN_PLAIN_CSV_VALUE
If a value written into a CSV file matches this regex, it will not be surrounded by " characters...
Collection< R > getArchetypes()
Returns a read-only collection of all Archetypes.
final ArchetypeSet< G, A, R > archetypeSet
The ArchetypeSet from which archetypes are shown.
void addAttribute(@NotNull final String attribute)
Adds a column for an attribute to the table.
BrowseArchetypesTableModel(@NotNull final ArchetypeSet< G, A, R > archetypeSet)
Creates a new instance.
static final Pattern PATTERN_QUOTE
A Pattern that matches a single " character.
String getArchetypeName()
Returns the name of this archetype.
void saveAsCsv(@NotNull final File file)
Saves the current contents as a CSV file.
String getAttributeString(@NotNull String attributeName, boolean queryArchetype)
Returns the requested attribute value of this GameObject as String.
static String getString(@NotNull final ActionBuilder actionBuilder, @NotNull final String key, @NotNull final String defaultValue)
Returns the value of a key.
A javax.swing.table.TableModel that displays a selected set of attributes within a list of archetypes...
Base package of all Gridarta classes.
Reflects a game object (object on a map).
Definition: GameObject.java:36
GameObjects are the objects based on Archetypes found on maps.
String getBestName()
Returns the name which is best appropriate to describe this GameObject.
Utility class for ActionBuilder related functions.
Class<?> getAttributeClass(@NotNull final String attribute)
Returns the class for rendering attribute values.
static void writeCsvValue(@NotNull final Writer writer, @NotNull final String value)
Writes a value into a CSV value.
Interface that captures similarities between different ArchetypeSet implementations.
void update()
Updates archetypes to include all archetypes from archetypeSet that define at least one show attribut...
String removeAttribute(final int index)
Removes an attribute from the table.
final Map< String, Class<?> > classMap
Caches mapping of attribute name to table column class.
final List< Class<?> > classes
The classes corresponding to attributes.