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.database;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Map;
24  
25  import net.sf.magicproject.clickable.targetable.card.CardModel;
26  import net.sf.magicproject.database.data.TranslatableData;
27  import net.sf.magicproject.management.MonitoredCheckContent;
28  import net.sf.magicproject.xml.XmlParser.Node;
29  
30  /***
31   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
32   * @since 0.90
33   */
34  class UrlTokenizer {
35  
36  	private List<String> constraints;
37  
38  	private List<String> streamStrings;
39  
40  	UrlTokenizer(Node node) {
41  		String stream = node.getAttribute("url");
42  		constraints = new ArrayList<String>();
43  		streamStrings = new ArrayList<String>();
44  		int constraintIndex = -1;
45  		int streamIndex = 0;
46  		while ((constraintIndex = stream.indexOf("${", constraintIndex)) != -1) {
47  			int closingBrace = stream.indexOf("}", constraintIndex);
48  			if (closingBrace == -1) {
49  				throw new InternalError("Unclosed '{' in stream : " + stream);
50  			}
51  			String constraint = stream.substring(constraintIndex + 2, closingBrace)
52  					.trim().toLowerCase();
53  			if (constraint.contains("+")) {
54  				// There is an inner contraint
55  				constraints
56  						.add(constraint.substring(0, constraint.indexOf('+')).trim());
57  				streamStrings.add(stream.substring(streamIndex, constraintIndex));
58  				streamStrings.add("%ADD%");
59  				constraints.add(constraint.substring(constraint.indexOf('+') + 1)
60  						.trim());
61  			} else {
62  				constraints.add(constraint);
63  				streamStrings.add(stream.substring(streamIndex, constraintIndex));
64  			}
65  			constraintIndex = closingBrace + 1;
66  			streamIndex = constraintIndex;
67  		}
68  		streamStrings.add(stream.substring(streamIndex));
69  	}
70  
71  	/***
72  	 * Given a scrore depending of accessible constraint within the given
73  	 * properties. Currently, score is
74  	 * <ul>
75  	 * <li><code>-1</code> for a not valid stream : constraints have not been
76  	 * all been found in the given properties</li>
77  	 * <li><code>0</code> for a valid stream : constraints have not all been
78  	 * found in the given properties</li>
79  	 * <li><code>positive number</code> for a valid stream with so much
80  	 * constraints (better) </li>
81  	 * </ul>
82  	 * 
83  	 * @param properties
84  	 *          set of available properties.
85  	 * @return the score.
86  	 */
87  	public int getUrlScore(Map<String, String> properties) {
88  		boolean hasCardNameAttr = false;
89  		for (String constraint : constraints) {
90  			if ("card.name".equals(constraint)) {
91  				hasCardNameAttr = true;
92  			} else {
93  				if (properties == null || !properties.containsKey(constraint)) {
94  					return -1;
95  				}
96  			}
97  		}
98  		if (properties == null) {
99  			return hasCardNameAttr ? 0 : 1;
100 		}
101 		return properties.size() + constraints.size() + (hasCardNameAttr ? 1 : 0);
102 	}
103 
104 	/***
105 	 * Return an URL built from the expression, card, constraints, and proxy.
106 	 * 
107 	 * @param cardModel
108 	 *          the card represented by the built url.
109 	 * @param properties
110 	 *          the properties used to replace requested properties within the
111 	 *          expression.
112 	 * @param proxy
113 	 *          the proxy containing the requested url. Would be used to get local
114 	 *          values.
115 	 * @return an URL built from the expression.
116 	 */
117 	public String getUrl(CardModel cardModel, Map<String, ?> properties,
118 			Proxy proxy) {
119 		final StringBuilder res = new StringBuilder(100);
120 		res.append(streamStrings.get(0));
121 		for (int i = 0; i < constraints.size(); i++) {
122 			final String constrainValue = getConstraint(constraints.get(i),
123 					cardModel, properties, proxy);
124 			if (constrainValue == null) {
125 				throw new RuntimeException("URL constraint #" + i + " '"
126 						+ this.constraints.get(i) + "' is not defined in given card ["
127 						+ cardModel + "] properties " + properties.values().toString());
128 			}
129 			if (i < constraints.size() - 1
130 					&& "%ADD%".equals(streamStrings.get(i + 1))) {
131 				res.append(String.valueOf(Integer.parseInt(constrainValue)
132 						+ Integer.parseInt(getConstraint(constraints.get(i + 1), cardModel,
133 								properties, proxy)) - 1));
134 				i++;
135 			} else {
136 				if (res.toString().contains(MonitoredCheckContent.STR_ZIP_PATH)) {
137 					res.append(constrainValue);
138 				} else {
139 					res.append(getJspUrl(constrainValue));
140 				}
141 			}
142 			res.append(streamStrings.get(i + 1));
143 		}
144 		return res.toString();
145 	}
146 
147 	private String getConstraint(String constraint, CardModel cardModel,
148 			Map<String, ?> constraints, Proxy proxy) {
149 		if ("card.name".equals(constraint)) {
150 			return cardModel.getCardName();
151 		}
152 		if ("card.keyname".equals(constraint)) {
153 			return cardModel.getKeyName();
154 		}
155 		if ("card.language".equals(constraint)) {
156 			return cardModel.getLanguage();
157 		}
158 		if (constraints == null || constraints.get(constraint) == null) {
159 			// the constraint does not exist
160 			return null;
161 		}
162 		final Object value = constraints.get(constraint);
163 		if (value instanceof String) {
164 			return (String) value;
165 		}
166 		if (proxy != null) {
167 			return proxy.getLocalValueFromGlobal(constraint,
168 					((TranslatableData) value).getValue());
169 		}
170 		return ((TranslatableData) value).getValue();
171 	}
172 
173 	/***
174 	 * Return the given string with all specials chars replaced by "%.." string
175 	 * 
176 	 * @param bruteString
177 	 *          the String to be parsed.
178 	 * @return the given string transformed to suit to an URL address.
179 	 */
180 	private String getJspUrl(String bruteString) {
181 		return bruteString.trim().replace(" ", "%20").replace("!", "%21").replace(
182 				"\"", "%22").replace("#", "%23").replace("&", "%26")
183 				.replace("'", "%27").replace("(", "%28").replace(")", "%29").replace(
184 						"*", "%2A").replace("+", "%2B").replace(",", "%2C").replace("-",
185 						"%2D").replace(".", "%2E").replace("/", "%2F").replace(":", "%3A")
186 				.replace(";", "%3B").replace("<", "%3C").replace("=", "%3D").replace(
187 						">", "%3E").replace("@", "%41").replace("^", "%5E").replace("_",
188 						"%60").replace("`", "%61").replace("{", "%7B").replace("}", "%7C")
189 				.replace("|", "%7D").replace("~", "%7E");
190 	}
191 
192 	@Override
193 	public String toString() {
194 		return "{streamStrings=" + streamStrings + ", constraints=" + constraints
195 				+ "}";
196 	}
197 
198 }