1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.export; 33 34 import com.jme3.animation.Animation; 35 import com.jme3.effect.shapes.*; 36 import com.jme3.material.MatParamTexture; 37 import java.io.IOException; 38 import java.lang.reflect.Field; 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.logging.Level; 43 import java.util.logging.Logger; 44 45 /** 46 * <code>SavableClassUtil</code> contains various utilities to handle 47 * Savable classes. The methods are general enough to not be specific to any 48 * particular implementation. 49 * Currently it will remap any classes from old paths to new paths 50 * so that old J3O models can still be loaded. 51 * 52 * @author mpowell 53 * @author Kirill Vainer 54 */ 55 public class SavableClassUtil { 56 57 private final static HashMap<String, String> classRemappings = new HashMap<String, String>(); 58 addRemapping(String oldClass, Class<? extends Savable> newClass)59 private static void addRemapping(String oldClass, Class<? extends Savable> newClass){ 60 classRemappings.put(oldClass, newClass.getName()); 61 } 62 63 static { 64 addRemapping("com.jme3.effect.EmitterSphereShape", EmitterSphereShape.class); 65 addRemapping("com.jme3.effect.EmitterBoxShape", EmitterBoxShape.class); 66 addRemapping("com.jme3.effect.EmitterMeshConvexHullShape", EmitterMeshConvexHullShape.class); 67 addRemapping("com.jme3.effect.EmitterMeshFaceShape", EmitterMeshFaceShape.class); 68 addRemapping("com.jme3.effect.EmitterMeshVertexShape", EmitterMeshVertexShape.class); 69 addRemapping("com.jme3.effect.EmitterPointShape", EmitterPointShape.class); 70 addRemapping("com.jme3.material.Material$MatParamTexture", MatParamTexture.class); 71 addRemapping("com.jme3.animation.BoneAnimation", Animation.class); 72 addRemapping("com.jme3.animation.SpatialAnimation", Animation.class); 73 } 74 remapClass(String className)75 private static String remapClass(String className) throws ClassNotFoundException { 76 String result = classRemappings.get(className); 77 if (result == null) { 78 return className; 79 } else { 80 return result; 81 } 82 } 83 isImplementingSavable(Class clazz)84 public static boolean isImplementingSavable(Class clazz){ 85 boolean result = Savable.class.isAssignableFrom(clazz); 86 return result; 87 } 88 getSavableVersions(Class<? extends Savable> clazz)89 public static int[] getSavableVersions(Class<? extends Savable> clazz) throws IOException{ 90 ArrayList<Integer> versionList = new ArrayList<Integer>(); 91 Class superclass = clazz; 92 do { 93 versionList.add(getSavableVersion(superclass)); 94 superclass = superclass.getSuperclass(); 95 } while (superclass != null && SavableClassUtil.isImplementingSavable(superclass)); 96 97 int[] versions = new int[versionList.size()]; 98 for (int i = 0; i < versionList.size(); i++){ 99 versions[i] = versionList.get(i); 100 } 101 return versions; 102 } 103 getSavableVersion(Class<? extends Savable> clazz)104 public static int getSavableVersion(Class<? extends Savable> clazz) throws IOException{ 105 try { 106 Field field = clazz.getField("SAVABLE_VERSION"); 107 Class<? extends Savable> declaringClass = (Class<? extends Savable>) field.getDeclaringClass(); 108 if (declaringClass == clazz){ 109 return field.getInt(null); 110 }else{ 111 return 0; // This class doesn't declare this field, e.g. version == 0 112 } 113 } catch (IllegalAccessException ex) { 114 IOException ioEx = new IOException(); 115 ioEx.initCause(ex); 116 throw ioEx; 117 } catch (IllegalArgumentException ex) { 118 throw ex; // can happen if SAVABLE_VERSION is not static 119 } catch (NoSuchFieldException ex) { 120 return 0; // not using versions 121 } 122 } 123 getSavedSavableVersion(Object savable, Class<? extends Savable> desiredClass, int[] versions, int formatVersion)124 public static int getSavedSavableVersion(Object savable, Class<? extends Savable> desiredClass, int[] versions, int formatVersion){ 125 Class thisClass = savable.getClass(); 126 int count = 0; 127 128 while (thisClass != desiredClass) { 129 thisClass = thisClass.getSuperclass(); 130 if (thisClass != null && SavableClassUtil.isImplementingSavable(thisClass)){ 131 count ++; 132 }else{ 133 break; 134 } 135 } 136 137 if (thisClass == null){ 138 throw new IllegalArgumentException(savable.getClass().getName() + 139 " does not extend " + 140 desiredClass.getName() + "!"); 141 }else if (count >= versions.length){ 142 if (formatVersion <= 1){ 143 return 0; // for buggy versions of j3o 144 }else{ 145 throw new IllegalArgumentException(savable.getClass().getName() + 146 " cannot access version of " + 147 desiredClass.getName() + 148 " because it doesn't implement Savable"); 149 } 150 } 151 return versions[count]; 152 } 153 154 /** 155 * fromName creates a new Savable from the provided class name. First registered modules 156 * are checked to handle special cases, if the modules do not handle the class name, the 157 * class is instantiated directly. 158 * @param className the class name to create. 159 * @param inputCapsule the InputCapsule that will be used for loading the Savable (to look up ctor parameters) 160 * @return the Savable instance of the class. 161 * @throws InstantiationException thrown if the class does not have an empty constructor. 162 * @throws IllegalAccessException thrown if the class is not accessable. 163 * @throws ClassNotFoundException thrown if the class name is not in the classpath. 164 * @throws IOException when loading ctor parameters fails 165 */ fromName(String className)166 public static Savable fromName(String className) throws InstantiationException, 167 IllegalAccessException, ClassNotFoundException, IOException { 168 169 className = remapClass(className); 170 try { 171 return (Savable) Class.forName(className).newInstance(); 172 } catch (InstantiationException e) { 173 Logger.getLogger(SavableClassUtil.class.getName()).log( 174 Level.SEVERE, "Could not access constructor of class ''{0}" + "''! \n" 175 + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", className); 176 throw e; 177 } catch (IllegalAccessException e) { 178 Logger.getLogger(SavableClassUtil.class.getName()).log( 179 Level.SEVERE, "{0} \n" 180 + "Some types need to have the BinaryImporter set up in a special way. Please doublecheck the setup.", e.getMessage()); 181 throw e; 182 } 183 } 184 fromName(String className, List<ClassLoader> loaders)185 public static Savable fromName(String className, List<ClassLoader> loaders) throws InstantiationException, 186 IllegalAccessException, ClassNotFoundException, IOException { 187 if (loaders == null) { 188 return fromName(className); 189 } 190 191 String newClassName = remapClass(className); 192 synchronized(loaders) { 193 for (ClassLoader classLoader : loaders){ 194 try { 195 return (Savable) classLoader.loadClass(newClassName).newInstance(); 196 } catch (InstantiationException e) { 197 } catch (IllegalAccessException e) { 198 } 199 200 } 201 } 202 203 return fromName(className); 204 } 205 } 206