Java tutorial
/* * Firemox is a turn based strategy simulator * Copyright (C) 2003-2007 Fabrice Daugan * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.sf.firemox.database; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.firemox.clickable.target.card.CardModel; import net.sf.firemox.database.data.TranslatableData; import net.sf.firemox.database.propertyconfig.PropertyConfig; import net.sf.firemox.database.propertyconfig.PropertyProxyConfig; import net.sf.firemox.expression.IntValue; import net.sf.firemox.token.IdConst; import net.sf.firemox.tools.MToolKit; import net.sf.firemox.ui.i18n.LanguageManager; import net.sf.firemox.xml.XmlParser; import net.sf.firemox.xml.XmlParser.Node; import org.apache.commons.lang.StringUtils; import org.xml.sax.SAXException; /** * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a> * @author <a href="mailto:kismet-sl@users.sourceforge.net">Stefano "Kismet" * Lenzi</a> * @since 0.90 */ public class Proxy { /** * Private declaration of this proxy. */ private final Map<String, Map<String, String>> aliases; /** * The stream base of stream URLs. */ private String streamBaseUrl; /** * The URL where data would be build from. */ private List<UrlTokenizer> streams; /** * The language of this proxy. */ private String language; /** * The encoding of stream retrieved from the proxy URL. */ private String encoding; /** * The proxy's name as displayed in the GUI menus */ private String name; /** * The proxy web site providing the data. Is only here for information * purpose. */ private String home; /** * The pictures configuration of this stream. */ public List<PictureConfiguration> pictures = new ArrayList<PictureConfiguration>(); /** * The properties managed by this proxy. */ private List<PropertyConfig> properties; /** * The XML name of this proxy. The name corresponds to the XML file definition * of this proxy */ private String xmlName; /** * The proxy's name as displayed in the GUI menus * * @return The proxy's name as displayed in the GUI menus */ public String getName() { return name; } @Override public String toString() { return getName(); } /** * Return the XML name of this proxy. The name corresponds to the XML file * definition of this proxy * * @return the XML name of this proxy. */ public String getXmlName() { return xmlName; } /** * Create a new instance of this class. * * @param xmlFile * the definition file of this proxy. * @throws IOException * If some other I/O error occurs * @throws SAXException * If some XML parse error occurs */ public Proxy(File xmlFile) throws IOException, SAXException { final XmlParser parser = new XmlParser(); final Node config = parser.parse(new FileInputStream(xmlFile)); xmlName = StringUtils.removeEnd(xmlFile.getName().toLowerCase(), ".xml"); name = config.getAttribute("name"); encoding = config.getAttribute("encoding"); language = config.getAttribute("language"); home = config.getAttribute("home"); final List<?> pictures = config.get("pictures").getNodes("picture"); for (int i = 0; i < pictures.size(); i++) { final Node pictureStream = (Node) pictures.get(i); this.pictures.add( new PictureConfiguration(new UrlTokenizer(pictureStream), pictureStream.getAttribute("base"))); } // read private aliases final Node aliases = config.get("alias"); this.aliases = new HashMap<String, Map<String, String>>(); if (aliases != null) { List<Node> nodes = aliases.getNodes("alias"); for (int i = nodes.size(); i-- > 0;) { final Node alias = nodes.get(i); Map<String, String> nameSpace = this.aliases.get(alias.getAttribute("property")); if (nameSpace == null) { nameSpace = new HashMap<String, String>(); this.aliases.put(alias.getAttribute("property"), nameSpace); } nameSpace.put(alias.getAttribute("local-value").toLowerCase(), alias.getAttribute("ref")); } } // read streams configurations Node dataConfig = config.get("data"); Node streamConfig = dataConfig.get("streams"); if (streamConfig != null) { streamBaseUrl = streamConfig.getAttribute("base"); final List<Node> streamsNode = streamConfig.getNodes("stream"); streams = new ArrayList<UrlTokenizer>(streamsNode.size()); for (int i = 0; i < streamsNode.size(); i++) { streams.add(new UrlTokenizer(streamsNode.get(i))); } if (streams.isEmpty()) { throw new RuntimeException("At least one stream configuration must be defined"); } // read properties configurations final List<Node> nodes = dataConfig.get("properties").getNodes("property"); properties = new ArrayList<PropertyConfig>(nodes.size()); for (int i = 0; i < nodes.size(); i++) { properties.add(new PropertyProxyConfig(nodes.get(i))); } } else { streams = new ArrayList<UrlTokenizer>(0); properties = new ArrayList<PropertyConfig>(0); } } /** * @param cardModel * the card model. * @param constraints * the constraints. * @return the string read from one of the streams of this proxy. * @throws IOException * If some other I/O error occurs */ private String getStringFromStream(CardModel cardModel, Map<String, String> constraints) throws IOException { // Determine the best stream configuration int highestScore = -1; UrlTokenizer stream = null; for (int i = 0; i < streams.size(); i++) { int score = streams.get(i).getUrlScore(constraints); if (score > highestScore) { highestScore = score; stream = streams.get(i); } } // No stream available for this proxy + card if (stream == null) { return null; } // read stream from the built URL final URL mainPage = new URL(streamBaseUrl + stream.getUrl(cardModel, constraints, this)); final StringBuilder res = new StringBuilder(2000); final InputStream proxyStream; try { proxyStream = MToolKit.getHttpConnection(mainPage).getInputStream(); } catch (Throwable e) { // Error during the IP get throw new IOException(LanguageManager.getString("error.stream.null")); } if (proxyStream == null) { // Error during the IP get throw new IOException(LanguageManager.getString("error.stream.null")); } final BufferedReader br = new BufferedReader(new InputStreamReader(proxyStream, encoding)); String line = null; while ((line = br.readLine()) != null) { res.append(StringUtils.trim(line)); } return res.toString(); } /** * Create a new DatabaseCard from the given CardModel. The best stream is * determined depending the given constraints. The associated picture is also * will be downloaded and loaded only during the first display of this * picture. * * @param cardModel * the object containing the card name. * @param constraints * set of constraints * @return a new DatabaseCard from the given CardModel. * @throws IOException * If some other I/O error occurs */ public DatabaseCard getDatabaseFromStream(CardModel cardModel, Map<String, String> constraints) throws IOException { final DatabaseCard databaseCard = new DatabaseCard(cardModel, this, DatabaseFactory.pictureProxies); // Find the best stream final String stream = getStringFromStream(cardModel, constraints); // Add parsed properties managed by this proxy PropertyProxyConfig.values.clear(); PropertyProxyConfig.values.put("%last-offset", new IntValue(0)); for (PropertyConfig property : properties) { final TranslatableData data = property.parseProperty(cardModel.getCardName(), stream, this); if (data != null) { databaseCard.add(data); } } return databaseCard; } /** * Return the encoding of stream retrieved from the proxy URL.<br> * Unreferenced method, but called with reflection. * * @return the encoding of stream retrieved from the proxy URL. */ public String getEncoding() { return encoding; } /** * Return the proxy web site providing the data. Is only here for information * purpose.<br> * Unreferenced method, but called with reflection. * * @return the proxy web site providing the data. */ public String getHome() { return home; } /** * Return the language of this proxy.<br> * Unreferenced method, but called with reflection. * * @return the language of this proxy. */ public String getLanguage() { return language; } /** * Return the referenced value of this proxy. If this proxy do not define the * named alias, return the given <param>localValue</param>. * * @param nameSpace * the name space (property) * @param localValue * the private-proxy value * @return the referenced value of this proxy. */ public String getGlobalValueFromLocal(String nameSpace, String localValue) { if (localValue.length() > 1000) { // This property may contains some invalid data return "Too long text (" + localValue.length() + ")"; } final Map<String, String> alias = aliases.get(nameSpace); if (alias != null && alias.containsKey(localValue.toLowerCase())) { return alias.get(localValue.toLowerCase()); } return localValue; } /** * Return the referenced value of this proxy. If this proxy do not define the * named alias, return the given <param>localValue</param>. * * @param nameSpace * @param localValue * @return the referenced value of this proxy. */ public String getLocalValueFromGlobal(String nameSpace, String localValue) { final Map<String, String> alias = aliases.get(nameSpace); if (alias != null && alias.containsValue(localValue.toLowerCase())) { for (Map.Entry<String, String> entry : alias.entrySet()) { if (entry.getValue().equals(localValue.toLowerCase())) { return entry.getKey(); } } } return localValue; } /** * Return a list of remote picture paths considering proxy, properties and the * given card. The returned order corresponds to the priority. * * @param cardModel * the card model. * @param data * the translated data. * @return a list of remote picture paths. */ public List<String> getRemotePictures(CardModel cardModel, Map<String, TranslatableData> data) { List<String> res = new ArrayList<String>(pictures.size()); for (PictureConfiguration p : pictures) { try { final String localUrl = p.getPictureUrl().getUrl(cardModel, data, this); res.add(p.getProxyBaseUrl() + "/" + localUrl); } catch (Exception e) { // Ignore this error, we'll not add this URL res.add(null); } } return res; } /** * Return a list of local picture paths considering proxy, properties and the * given card. The returned order corresponds to the priority. * * @param cardModel * the card model. * @param data * the translated data. * @return a list of local picture paths. */ public List<String> getLocalPictures(CardModel cardModel, Map<String, TranslatableData> data) { List<String> res = new ArrayList<String>(pictures.size()); for (PictureConfiguration p : pictures) { try { final String localUrl = p.getPictureUrl().getUrl(cardModel, data, this); res.add(MToolKit.getTbsPicture( IdConst.PROXIES_LOCATION + "/" + getValidPath(getXmlName()) + "/" + localUrl, false)); } catch (Exception e) { // Ignore this error, we'll not add this URL res.add(null); } } return res; } /** * Simple method returning a suitable path for Windows, Linux,... * * @param path * any non null string * @return a suitable path for Windows, Linux,... */ private String getValidPath(String path) { return path.replace('/', '_').replace(':', '_').replace('*', '_').replace('\\', '_').replace('%', '_') .replace('"', '_').replace('\'', '_').replace('&', '_').replace('$', '_').replace('~', '_'); } }