package ch.sahits.game.graphic.image.impl;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.apache.log4j.Logger;
import ch.sahits.game.graphic.image.ILanguageAwareImageLoader;
import ch.sahits.game.graphic.image.NamedBufferedImage;
/**
* This imageLoader augments the {@link ImagesLoader} by the
* ability to load images based on a language.
* * <br/>
* ImagesFile Formats:<br/>
*
* <pre>
* o <fnm> // a single image file
*
* l <fnm*.ext> {<coords>} // a single language specific file
*
* n <fnm*.ext> <number> // a series of numbered image files, whose
* // filenames use the numbers 0 - <number>-1
*
* s <fnm> <number> // a strip file (fnm) containing a single row
* // of <number> images
*
* g <name> <fnm> [ <fnm> ]* // a group of files with different names;
* // they are accessible via
* // <name> and position _or_ <fnm> prefix
*
* L <fnm*.ext> // load all matching files for a language
* </pre>
*
* and blank lines and comment lines.<br/>
* <br/>
* The language specific files are looked up by replacing the the wildcard with the local string and
* then loading the image. If the image cannot be found the default image (without local extension) will
* be loaded.<br/>
* If the local is 'de_CH' and the filename specified is 'screenshot*.png' then the following files are
* tried to load:
* <ul>
* <li>screenshot_de_CH.png</li>
* <li>screenshot_de.png</li>
* <li>screenshot.png</li>
* </ul>
* The first file that can be loaded will be returned.<br/>
* The file name is followed by an optional list of points defining a polygon that can be used as a
* map for the image. The coordinates are entered in the form x-y. Multiple coordinates are separated
* by a space.
* <br/>
* The numbered image files (n) can be accessed by the <fnm> prefix and
* <number>. <br/>
* <br/>
* The strip file images can be accessed by the <fnm> prefix and their position
* inside the file (which is assumed to hold a single row of images).<br/>
* <br/>
* The images in group files can be accessed by the 'g' <name> and the
* <fnm> prefix of the particular file, or its position in the group.<br/>
* <br/>
* <br/>
* The images are stored as BufferedImage objects, so they will be manipulated
* as 'managed' images by the JVM (when possible).
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Feb 26, 2011
*
*/
public class LanguageImageLoader extends ImagesLoader implements ILanguageAwareImageLoader{
private final static Logger logger = Logger.getLogger(LanguageImageLoader.class);
private static final String EMPTY = "";
private final Locale locale;
// TODO the polygon coordinate should be defined in a separate file since they are language specific ImageData
private HashMap<String,Polygon> polyMap = new HashMap<String,Polygon>();
private HashMap<String,List<NamedBufferedImage>> localImageMap = new HashMap<String, List<NamedBufferedImage>>();
public LanguageImageLoader(String fnm, Locale locale) {
initLoader();
this.locale=locale;
loadImagesFile(fnm);
}
/**
* Parse the definition file and load the corresponding images
* @param br
* @throws IOException
*/
protected void parseImageDefinitionsAndLoad(BufferedReader br)
throws IOException {
String line;
char ch;
while ((line = br.readLine()) != null) {
if (line.length() == 0) // blank line
continue;
if (line.startsWith("//")) // comment
continue;
ch = line.charAt(0);
if (ch == 'o') // a single image
getFileNameImage(line);
else if (ch == 'l') // a local image
getLocalFileNameImage(line);
else if (ch == 'n') // a numbered sequence of images
getNumberedImages(line);
else if (ch == 's') // an images strip
getStripImages(line);
else if (ch == 'g') // a group of images
getGroupImages(line);
else if (ch == 'L') // Lanuguage group
getLanguageGroupImages(line);
else
logger.warn("Do not recognize line: " + line);
}
}
/**
* Loading the image group with the language specification
* @param line
*/
private void getLanguageGroupImages(String line) {
String[] locales = getLocals();
StringTokenizer tokens = new StringTokenizer(line);
if (tokens.countTokens()==2){
tokens.nextToken(); // skip command label
System.out.print("L Line: ");
String filePrototype = tokens.nextToken();
String head = filePrototype.substring(0, filePrototype.indexOf('*'));
String tail = filePrototype.substring(filePrototype.indexOf('*')+1);
for (int i = 0; i < locales.length; i++) {
String fname = head+"*"+locales[i]+tail;
String[] fnames = getAvailableFilenames(fname);
Arrays.sort(fnames);
if (fnames.length>0){
ArrayList<NamedBufferedImage> images = new ArrayList<NamedBufferedImage>();
for (int j = 0; j < fnames.length; j++) {
String name = head+j;
BufferedImage img = loadImage(fnames[j]);
logger.debug(" Stored " + name + "/" + fnames[j]);
images.add(new NamedBufferedImage(img, name));
}
localImageMap.put(head, images);
break;
}
}
}
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.ILanguageAwareImageLoader#getLocalImageGroup(java.lang.String, int)
*/
@Override
public BufferedImage getLocalImageGroup(String groupName,int index){
List<NamedBufferedImage> img =localImageMap.get(groupName);
if (img!=null && img.size()>=index){
return img.get(index).getImage();
} else {
return null;
}
}
@Override
public int getLocalImageGroupeSize(String groupName){
List<NamedBufferedImage> img =localImageMap.get(groupName);
if (img!=null){
return img.size();
} else {
return 0;
}
}
/**
* Retrieve the list of locales filename parts that are to be checked. The parts are ordered.
* @return
*/
private String[] getLocals() {
String lang_country = locale.toString();
String localReplacement;
if (lang_country.startsWith("_")) {// no language defined
localReplacement=EMPTY;
}else if (lang_country.indexOf("__")>0) {// no country
localReplacement=lang_country.substring(0,lang_country.indexOf("__"));
} else if (lang_country.indexOf('_')!=lang_country.lastIndexOf('_')){ // with variant
localReplacement=lang_country.substring(0,lang_country.lastIndexOf('_'));
} else if (lang_country.indexOf('_')==lang_country.lastIndexOf('_') && lang_country.indexOf('_')!=-1){
String[] parts = lang_country.split("_");
localReplacement=parts[0]+"_"+parts[1].toUpperCase();
} else if (lang_country.length()>0&& lang_country.indexOf('_')==-1){ // only country
localReplacement=lang_country;
}else {
localReplacement=EMPTY;
}
if (localReplacement.equals(EMPTY)){
return new String[]{EMPTY};
} else if (localReplacement.indexOf('_')>0){
return new String[]{localReplacement,localReplacement.substring(0,localReplacement.indexOf('_')),EMPTY};
} else {
return new String[]{localReplacement,EMPTY};
}
}
protected final void getLocalFileNameImage(String line) {
StringTokenizer tokens = new StringTokenizer(line);
String storeName=null;
if (tokens.countTokens() < 2)
logger.warn("Wrong no. of arguments for " + line);
else {
tokens.nextToken(); // skip command label
StringBuilder sb = new StringBuilder();
sb.append("l Line: ");
String filePrototype = tokens.nextToken();
String fileName;
if (filePrototype.indexOf('*')>0){
fileName = filePrototype.substring(0, filePrototype.indexOf('*'));
} else {
fileName = filePrototype.substring(0, filePrototype.lastIndexOf('.'));
}
if (fileName.indexOf('.')>0){
fileName=fileName.substring(0, fileName.indexOf('.'));
}
String ext = filePrototype.substring(filePrototype.indexOf('.'));
String lang_country = locale.toString();
String localReplacement;
if (lang_country.startsWith("_")) {// no language defined
localReplacement=EMPTY;
}else if (lang_country.indexOf("__")>0) {// no country
localReplacement=lang_country.substring(0,lang_country.indexOf("__"));
} else if (lang_country.indexOf('_')!=lang_country.lastIndexOf('_')){ // with variant
localReplacement=lang_country.substring(0,lang_country.lastIndexOf('_'));
} else if (lang_country.indexOf('_')==lang_country.lastIndexOf('_') && lang_country.indexOf('_')!=-1){
String[] parts = lang_country.split("_");
localReplacement=parts[0]+"_"+parts[1].toUpperCase();
} else if (lang_country.length()>0&& lang_country.indexOf('_')==-1){ // only country
localReplacement=lang_country;
}else {
localReplacement=EMPTY;
}
if (localReplacement.indexOf("_")>0){ // try loading language_country
String tmp = fileName+"_"+localReplacement+ext;
if (isAvailable(tmp)){
storeName=loadSingleLanguageImage(tmp,"_"+localReplacement,ext);
} else {
localReplacement=localReplacement.substring(0, localReplacement.indexOf('_'));
}
}
if (!localReplacement.equals(EMPTY) && storeName==null){ // try load language
String tmp = fileName+"_"+localReplacement+ext;
if (isAvailable(tmp)){
storeName=loadSingleLanguageImage(tmp,"_"+localReplacement,ext);
} else {
localReplacement=EMPTY;
}
}
if (localReplacement.equals(EMPTY) && storeName==null){ // load default
String tmp = fileName+ext;
storeName=loadSingleLanguageImage(tmp,"",ext);
}
if (tokens.countTokens()==1){
String definitionFile = tokens.nextToken();
String polyfile = definitionFile.substring(0,definitionFile.indexOf("."))+"_"+localReplacement+definitionFile.substring(definitionFile.indexOf("."));
loadPolygonData(storeName,polyfile);
}
}
// if (tokens.countTokens()>2){
// // There are coordinates of a polygon
// Polygon poly = new Polygon();
// while (tokens.hasMoreTokens()){
// String coords = tokens.nextToken();
// String[] parts = coords.split("-");
// poly.addPoint(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
// }
// polyMap.put(storeName, poly);
// }
}
private void loadPolygonData(String storeName, String polyfile) {
String imsFNm = IMAGE_DIR + polyfile;
logger.info("Reading file: " + imsFNm);
try {
InputStream in = this.getClass().getResourceAsStream(imsFNm);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// BufferedReader br = new BufferedReader( new FileReader(imsFNm));
String line;
while ((line = br.readLine()) != null) {
if (line.length() == 0) // blank line
continue;
if (line.startsWith("//")) // comment
continue;
if (line.trim().startsWith(storeName)){
StringTokenizer tokens = new StringTokenizer(line.trim());
if (tokens.countTokens()<4){
logger.warn("Wrong no. of arguments for " + line);
} else {
tokens.nextToken(); // skip name
Polygon poly = new Polygon();
while (tokens.hasMoreTokens()){
String coords = tokens.nextToken();
String[] parts = coords.split("-");
poly.addPoint(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
}
polyMap.put(storeName, poly);
}
} else {
continue;
}
}
br.close();
} catch (IOException e) {
logger.fatal("Error reading file: " + imsFNm, e);
System.exit(1);
}
}
/**
* Check if the image file has a polygon defined.
* @param fnm filename under which the image is stored
* @return
*/
public boolean hasPolygonDefined(String fnm){
return polyMap.containsKey(fnm);
}
/**
* Retrieve the ploygon for the image file.
* If no polygon is defined for the image null
* is returned
* @param fnm filename under which the image is stored
* @return
*/
public Polygon getPolygon(String fnm){
return polyMap.get(fnm);
}
/**
* Load a language file
* @param fnm complete filename
* @param loc Locale part
* @param ext file extension
* @return Base name of the file, if it can be loaded else null
*/
private String loadSingleLanguageImage(String fnm,String loc,String ext) {
String name = getPrefix(fnm,loc,ext);
if (imagesMap.containsKey(name)) {
logger.warn("Error: " + name + "already used");
return null;
}
BufferedImage bi = loadImage(fnm);
if (bi != null) {
ArrayList<BufferedImage> imsList = new ArrayList<BufferedImage>();
imsList.add(bi);
imagesMap.put(name, imsList);
logger.debug(" Stored " + name + "/" + fnm);
return name;
} else
return null;
}
private String getPrefix(String fnm, String loc, String ext) {
if (loc.equals(EMPTY)){
return fnm.substring(0, fnm.indexOf(ext));
} else {
return fnm.substring(0, fnm.lastIndexOf(loc));
}
}
}