Home | History | Annotate | Download | only in export
      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 
     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 
     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 
     84     public static boolean isImplementingSavable(Class clazz){
     85         boolean result = Savable.class.isAssignableFrom(clazz);
     86         return result;
     87     }
     88 
     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 
    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 
    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      */
    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 
    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