View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * 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   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 		// sort the cards A -> Z
163 		Collections.sort(cards);
164 
165 		// Resolve real card names
166 		List<MCardCompare> cardErrors = new ArrayList<MCardCompare>(cards.size());
167 		for (MCardCompare card : cards) {
168 			if (null == updateRealCardName(card, dbStream)) {
169 				// This card doesn't exist
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 		// Now, validate the deck against the given constraint
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 		 * search this card now in the data base until we found it or we or greater
226 		 * name was found.
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 				// card is found
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 				// card doesn't exist, return the over read bytes
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 }