1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.magicproject.deckbuilder;
20
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28
29 import net.sf.magicproject.expression.DeckCounter;
30 import net.sf.magicproject.tools.Log;
31 import net.sf.magicproject.tools.MCardCompare;
32 import net.sf.magicproject.tools.MToolKit;
33 import net.sf.magicproject.ui.i18n.LanguageManager;
34
35 /***
36 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
37 * @since 0.90
38 */
39 public class Deck {
40
41 /***
42 * The last loaded card name
43 */
44 private static String lastCardName = null;
45
46 /***
47 * Is this deck has been validated.
48 */
49 private boolean isValidated;
50
51 /***
52 * The offset corresponding to the last card read.
53 */
54 private static long lastCardPosition = 0L;
55
56 /***
57 * The current instance of deck. Used by some test such as {@link DeckCounter}.
58 * <br>
59 * This field should be removed to be added as parameter of this kind of test.
60 *
61 * @see #validate(String)
62 */
63 public static Deck currentDeck;
64
65 /***
66 * The deck name
67 */
68 private final String deckName;
69
70 /***
71 * The cards of this deck.
72 */
73 private final List<MCardCompare> cards;
74
75 /***
76 * The additional properties of this deck.
77 */
78 private Map<String, Object> properties;
79
80 /***
81 * Create a new instance of this class.
82 *
83 * @param deckName
84 * The deck name.
85 */
86 public Deck(String deckName) {
87 this.deckName = deckName;
88 this.cards = new ArrayList<MCardCompare>();
89 }
90
91 /***
92 * Return the cards of this deck.
93 *
94 * @return the cards of this deck.
95 */
96 public List<MCardCompare> getCards() {
97 return cards;
98 }
99
100 /***
101 * Return a property value of this deck.
102 *
103 * @param propertyName
104 * the property name.
105 * @return a property. Null if the property does not exist.
106 */
107 public Object getProperty(String propertyName) {
108 if (properties == null) {
109 return null;
110 }
111 return properties.get(propertyName);
112 }
113
114 /***
115 * Add any number of cards to this deck.
116 *
117 * @param cardName
118 * the card name.
119 * @param amount
120 * th numbe of cards to add to this deck.
121 * @param properties
122 * the optional properties attached to these cards.
123 */
124 public void addCard(String cardName, int amount,
125 Map<String, String> properties) {
126 if (this.properties == null && properties == null) {
127 for (MCardCompare card : cards) {
128 if (card.getName().equals(cardName)) {
129 card.add(amount);
130 return;
131 }
132 }
133 }
134 cards.add(new MCardCompare(cardName, amount, properties, 0L));
135 }
136
137 /***
138 * Add any number of cards to this deck.
139 *
140 * @param card
141 * the card specification.
142 */
143 public void addCard(MCardCompare card) {
144 cards.add(card);
145 }
146
147 /***
148 * Validate this deck.
149 *
150 * @param deckConstraint
151 * the deck constraint name.
152 * @return list of validation error. Validation is ok when this list is empty.
153 * @throws IOException
154 * if io exception occurred.
155 */
156 public List<String> validate(DeckConstraint deckConstraint)
157 throws IOException {
158 Log.info("Validating deck...");
159 final List<String> errors = new ArrayList<String>();
160 final FileInputStream dbStream = MdbLoader.resetMdb();
161
162
163 Collections.sort(cards);
164
165
166 List<MCardCompare> cardErrors = new ArrayList<MCardCompare>(cards.size());
167 for (MCardCompare card : cards) {
168 if (null == updateRealCardName(card, dbStream)) {
169
170 cardErrors.add(card);
171 }
172 }
173 if (!cardErrors.isEmpty()) {
174 cards.removeAll(cardErrors);
175 StringBuilder builderError = new StringBuilder();
176 for (MCardCompare card : cardErrors) {
177 if (builderError.length() != 0) {
178 builderError.append(", \n ");
179 }
180 builderError.append(card.getName());
181 }
182 errors.add(LanguageManager.getString("notyetimplemented", builderError
183 .toString()));
184 }
185
186
187 this.deckConstraint = deckConstraint;
188 if (deckConstraint != null) {
189 errors.addAll(deckConstraint.validate(this));
190 }
191 return errors;
192 }
193
194 /***
195 * Validate this deck.
196 *
197 * @param deckConstraint
198 * the deck constraint name.
199 * @return list of validation error. Validation is ok when this list is empty.
200 * @throws IOException
201 * if io exception occurred.
202 */
203 public List<String> validate(String deckConstraint) throws IOException {
204 if (deckConstraint != null) {
205 return validate(DeckConstraints.getDeckConstraint(deckConstraint));
206 }
207 return validate((DeckConstraint) null);
208 }
209
210 /***
211 * Set the current offset of the specified stream, to the first byte of the
212 * specified card and return the real card name.
213 *
214 * @param card
215 * the card configuration.
216 * @param dbStream
217 * opened file read containing the available cards
218 * @return the real card name if exist,null if card has not been found.
219 * @throws IOException
220 * if io exception occurred.
221 */
222 private static String updateRealCardName(MCardCompare card,
223 FileInputStream dbStream) throws IOException {
224
225
226
227
228 String cardName = card.getName();
229 String keyName = MToolKit.getKeyName(cardName);
230 final long currentOffset = dbStream.getChannel().position();
231 if (lastCardName != null
232 && MToolKit.getKeyName(lastCardName).compareTo(keyName) == 0) {
233 dbStream.getChannel().position(lastCardPosition);
234 card.setName(lastCardName);
235 card.setMdbOffset(MToolKit.readInt24(dbStream));
236 return lastCardName;
237 }
238 if (lastCardName == null
239 || MToolKit.getKeyName(lastCardName).compareTo(keyName) > 0) {
240 MdbLoader.resetMdb();
241 }
242 while (true) {
243 final String realCard = MToolKit.readString(dbStream);
244 final int result = MToolKit.getKeyName(realCard).compareTo(keyName);
245 if (result == 0) {
246
247 lastCardName = realCard;
248 lastCardPosition = dbStream.getChannel().position();
249 card.setName(realCard);
250 card.setMdbOffset(MToolKit.readInt24(dbStream));
251 return realCard;
252 } else if (result > 0) {
253
254 dbStream.getChannel().position(currentOffset);
255 return null;
256 } else {
257 dbStream.getChannel().position(dbStream.getChannel().position() + 3);
258 }
259 }
260 }
261
262 @Override
263 public String toString() {
264 return deckName;
265 }
266
267 /***
268 * Return the deck constraint applied to this deck.May be <code>null</code>.
269 *
270 * @return the deck constraint applied to this deck.May be <code>null</code>.
271 */
272 public DeckConstraint getConstraint() {
273 return deckConstraint;
274 }
275
276 /***
277 * The deck constraint applied to this deck.May be <code>null</code>.
278 */
279 private DeckConstraint deckConstraint;
280
281 /***
282 * Write the cards of this deck into the given output stream.
283 *
284 * @param out
285 * the output stream where this deck will be written.
286 */
287 public void send(OutputStream out) {
288 for (MCardCompare card : cards) {
289 MToolKit.writeString(out, card.toString());
290 }
291 }
292
293 /***
294 * Return available stream of deck. Not <code>null</code> only when
295 * validating a deck. The returned stream corresponds to the first offset of
296 * first card.
297 *
298 * @return Available stream of deck. Not <code>null</code> only when
299 * validating a deck.
300 */
301 public FileInputStream getMdbStream() {
302 return MdbLoader.resetMdb();
303 }
304
305 /***
306 * Is this deck has been validated.
307 *
308 * @return <code>true</code> if this deck has been validated.
309 */
310 public boolean isValidated() {
311 return isValidated;
312 }
313
314 /***
315 * @param card
316 */
317 public void removeCard(MCardCompare card) {
318 getCards().remove(card);
319 }
320 }