/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* eAdventure 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package ead.importer.resources;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import es.eucm.ead.model.interfaces.features.ResourcedEvented;
import es.eucm.ead.model.assets.AssetDescriptor;
import es.eucm.ead.model.assets.drawable.basics.Image;
import es.eucm.ead.model.assets.drawable.basics.animation.Frame;
import es.eucm.ead.model.assets.drawable.basics.animation.FramesAnimation;
import es.eucm.ead.model.elements.ResourcedElement;
import es.eucm.ead.model.elements.conditions.EmptyCond;
import es.eucm.ead.model.elements.effects.TriggerMacroEf;
import es.eucm.ead.model.elements.events.SceneElementEv;
import es.eucm.ead.model.elements.events.enums.SceneElementEvType;
import es.eucm.ead.model.elements.extra.EAdList;
import es.eucm.ead.model.elements.predef.effects.ChangeAppearanceEf;
import ead.importer.EAdElementImporter;
import ead.importer.GenericImporter;
import ead.importer.interfaces.ResourceImporter;
import es.eucm.eadventure.common.data.animation.Animation;
import es.eucm.eadventure.common.data.animation.ImageLoaderFactory;
import es.eucm.eadventure.common.data.chapter.conditions.Conditions;
import es.eucm.eadventure.common.data.chapter.resources.Resources;
import es.eucm.eadventure.common.loader.InputStreamCreator;
import es.eucm.eadventure.common.loader.Loader;
/**
* Resource Importer
*
*
*/
@Singleton
public class ResourceImporterImpl implements ResourceImporter {
static private Logger logger = LoggerFactory
.getLogger(ResourceImporterImpl.class);
/**
* Conditions importer
*/
private EAdElementImporter<Conditions, Condition> conditionsImporter;
/**
* Animation importer
*/
private GenericImporter<Animation, FramesAnimation> animationImporter;
private ImageLoaderFactory imageLoader;
private InputStreamCreator inputStreamCreator;
/**
* Absolute path where the new adventure must be placed
*/
private String newAdventurePath;
/**
* Stores correspondences between Strings at old adventure project and StringS at
* new adventure project
*/
private Map<String, String> urisCorrespondences;
/**
* A map matching old uris with the new assets
*/
private Map<String, AssetDescriptor> assets;
@Inject
public ResourceImporterImpl(
EAdElementImporter<Conditions, Condition> conditionsImporter,
GenericImporter<Animation, FramesAnimation> animationImporter,
ImageLoaderFactory imageLoader,
InputStreamCreator inputStreamCreator) {
this.imageLoader = imageLoader;
this.inputStreamCreator = inputStreamCreator;
this.conditionsImporter = conditionsImporter;
this.animationImporter = animationImporter;
urisCorrespondences = new LinkedHashMap<String, String>();
assets = new LinkedHashMap<String, AssetDescriptor>();
}
@Override
public void setPath(String newAdventurePath) {
this.newAdventurePath = newAdventurePath;
this.urisCorrespondences.clear();
this.assets.clear();
createFolders();
}
private void createFolders() {
File f = new File(newAdventurePath + "/drawable");
f.mkdirs();
f = new File(newAdventurePath + "/binary");
f.mkdirs();
}
@Override
public String getString(String oldString) {
String newString = urisCorrespondences.get(oldString);
if (newString == null) {
String folder = getFolder(oldString);
String fileName = oldString.replace("/", "_");
newString = folder + "/" + fileName;
if (!copyFile(oldString, newString)) {
logger.error("Missing resource: {}", oldString);
return null;
}
newString = "@" + newString;
urisCorrespondences.put(oldString, newString);
}
return newString;
}
private String getFolder(String oldString) {
if (oldString.endsWith(".png") || oldString.endsWith(".jpg"))
return DRAWABLE;
else
return BINARY;
}
@Override
public boolean copyFile(String oldString, String newString) {
File toResourceFile = new File(newAdventurePath, newString);
InputStream in = null;
OutputStream out = null;
boolean success = false;
try {
in = inputStreamCreator.buildInputStream(oldString);
if (in != null) {
out = new FileOutputStream(toResourceFile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
success = true;
}
} catch (Exception e) {
logger.error("Error copying '{}' to '{}'", oldString, newString);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
logger.error("Error accesing '{}'", oldString);
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
logger.error("Error accesing '{}'", newString);
}
}
}
return success;
}
@Override
public void importResources(ResourcedEvented element,
List<Resources> resources, Map<String, String> resourcesStrings,
Map<String, Object> resourcesObjectClasses) {
int i = 0;
// We iterate for the resources. Each resource is associated to some
// conditions.
List<String> bundles = new ArrayList<String>();
List<Condition> conditions = new ArrayList<Condition>();
for (Resources r : resources) {
String bundleId;
if (i == 0) {
bundleId = ResourcedElement.INITIAL_BUNDLE;
} else {
bundleId = "bundle_" + i;
}
bundles.add(bundleId);
Condition c = conditionsImporter.init(r.getConditions());
c = conditionsImporter.convert(r.getConditions(), c);
conditions.add(c);
for (String resourceType : resourcesStrings.keySet()) {
String assetPath = r.getAssetPath(resourceType);
Object o = resourcesObjectClasses.get(resourceType);
AssetDescriptor asset = null;
if (o instanceof Class) {
asset = this.getAssetDescritptor(assetPath, (Class<?>) o);
} else if (o instanceof List) {
asset = (AssetDescriptor) ((List<?>) o).get(i);
} else if (o instanceof AssetDescriptor) {
asset = (AssetDescriptor) o;
}
if (asset != null) {
String propertyName = resourcesStrings.get(resourceType);
element.addAsset(bundleId, propertyName, asset);
}
}
i++;
}
if (resources.size() > 0) {
setBundlesEvent(element, conditions, bundles);
}
}
public static void setBundlesEvent(ResourcedEvented element,
List<Condition> conditions, List<String> bundles) {
if ((conditions.size() == 1 || EmptyCond.TRUE.equals(conditions.get(0)))
&& bundles.size() >= 1) {
return;
} else {
int i = 0;
TriggerMacroEf changeBundle = new TriggerMacroEf();
for (Condition c : conditions) {
ChangeAppearanceEf effect = new ChangeAppearanceEf(null,
bundles.get(i));
changeBundle.putEffects(c, new EAdList<Effect>());
i++;
}
SceneElementEv event = new SceneElementEv(
SceneElementEvType.ALWAYS, changeBundle);
element.addEvent(event);
}
}
@Override
public AssetDescriptor getAssetDescritptor(String assetPath, Class<?> clazz) {
if (assetPath == null)
return null;
if (assets.containsKey(assetPath)) {
return assets.get(assetPath);
}
AssetDescriptor asset = null;
// Special case
if (assetPath.startsWith("assets/special/EmptyAnimation")) {
if (assetPath.endsWith("_01.png")) {
String uri = this.getString(assetPath);
return new Image(uri);
} else if (!(assetPath.endsWith(".eaa") || assetPath
.endsWith("_01.png")))
if (fileExists(assetPath + ".eaa"))
assetPath += ".eaa";
else if (fileExists(assetPath + "_01.png")) {
assetPath += "_01.png";
} else if (fileExists(assetPath + "_01.jpg")) {
assetPath += "_01.jpg";
} else {
logger.info("There was a problem with EmptyAnimation");
}
}
if (assetPath.startsWith("assets/animation")
|| assetPath.startsWith("assets/special/EmptyAnimation")) {
if (assetPath.endsWith(".eaa")
|| (getFileExtension(assetPath) == null && fileExists(assetPath
+ ".eaa"))) {
Animation a = Loader.loadAnimation(inputStreamCreator,
assetPath, imageLoader);
asset = animationImporter.init(a);
asset = animationImporter.convert(a, asset);
} else {
asset = importImagesAnimation(assetPath);
}
} else {
String newAssetPath = getString(assetPath);
try {
asset = (AssetDescriptor) clazz.getConstructor(String.class)
.newInstance(newAssetPath);
} catch (Exception e) {
logger.warn("Error while playing with AssetDescriptor {}",
newAssetPath, e);
}
}
if (asset != null)
assets.put(assetPath, asset);
return asset;
}
/**
* Imports an animation in the form _01.png, _02.png, or _01.jpg, _02.jpg,
* etc.
*
* @param assetPath
* the root asset path
* @return the asset
*/
private AssetDescriptor importImagesAnimation(String assetPath) {
String fileExtension = getFileExtension(assetPath);
if (fileExtension == null)
return null;
FramesAnimation frames = new FramesAnimation();
int frame = 1;
int frameTime = 500;
String oldPath = assetPath + "_0" + frame++ + fileExtension;
while (fileExists(oldPath)) {
String newPath = getString(oldPath);
frames.addFrame(new Frame(newPath, frameTime));
oldPath = assetPath + "_0" + frame++ + fileExtension;
}
if (frames.getFrameCount() > 0)
return frames;
else
return null;
}
private String getFileExtension(String assetPath) {
String prefix = "_01";
if (fileExists(assetPath + prefix + ".png")) {
return ".png";
} else if (fileExists(assetPath + prefix + ".jpg")) {
return ".jpg";
} else if (fileExists(assetPath + prefix + ".PNG")) {
return ".PNG";
} else if (fileExists(assetPath + prefix + ".JPG")) {
return ".JPG";
} else
return null;
}
@Override
public String getNewProjecFolder() {
return newAdventurePath;
}
public boolean fileExists(String oldString) {
boolean exists = false;
InputStream is = inputStreamCreator.buildInputStream(oldString);
if (is != null) {
exists = true;
try {
is.close();
} catch (IOException e) {
}
}
return exists;
}
public BufferedImage loadImage(String oldUri) {
try {
return ImageIO.read(inputStreamCreator.buildInputStream(oldUri));
} catch (Exception e) {
logger.error("Error loading {}", oldUri);
}
return null;
}
@Override
public Dimension getDimensionsForOldImage(String oldUri) {
if (oldUri != null) {
BufferedImage image = loadImage(oldUri);
if (image != null) {
Dimension d = new Dimension(image.getWidth(), image.getHeight());
image.flush();
return d;
}
}
return new Dimension(100, 100);
}
@Override
public Dimension getDimensionsForNewImage(String newString) {
BufferedImage image = this.getNewImage(newString);
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
} else {
return new Dimension(100, 100);
}
}
@Override
public BufferedImage getOldImage(String oldUri) {
return loadImage(oldUri);
}
@Override
public BufferedImage getNewImage(String newString) {
File toResourceFile = new File(newAdventurePath, newString.substring(1));
FileInputStream inputStream = null;
BufferedImage image = null;
try {
inputStream = new FileInputStream(toResourceFile);
image = ImageIO.read(inputStream);
} catch (Exception e) {
logger.error("Error loading {}", newString);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.error("Error closing input stream from {}",
newString);
}
}
}
return image;
}
public InputStreamCreator getInputStreamCreator() {
return this.inputStreamCreator;
}
}