All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.export.SavableClassUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2021 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.export;

import com.jme3.animation.Animation;
import com.jme3.effect.shapes.*;
import com.jme3.material.MatParamTexture;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * SavableClassUtil contains various utilities to handle
 * Savable classes. The methods are general enough to not be specific to any
 * particular implementation.
 * Currently it will remap any classes from old paths to new paths
 * so that old J3O models can still be loaded.
 *
 * @author mpowell
 * @author Kirill Vainer
 */
public class SavableClassUtil {

    private final static HashMap CLASS_REMAPPINGS = new HashMap<>();

    private static void addRemapping(String oldClass, Class newClass) {
        CLASS_REMAPPINGS.put(oldClass, newClass.getName());
    }

    static {
        addRemapping("com.jme3.effect.EmitterSphereShape", EmitterSphereShape.class);
        addRemapping("com.jme3.effect.EmitterBoxShape", EmitterBoxShape.class);
        addRemapping("com.jme3.effect.EmitterMeshConvexHullShape", EmitterMeshConvexHullShape.class);
        addRemapping("com.jme3.effect.EmitterMeshFaceShape", EmitterMeshFaceShape.class);
        addRemapping("com.jme3.effect.EmitterMeshVertexShape", EmitterMeshVertexShape.class);
        addRemapping("com.jme3.effect.EmitterPointShape", EmitterPointShape.class);
        addRemapping("com.jme3.material.Material$MatParamTexture", MatParamTexture.class);
        addRemapping("com.jme3.animation.BoneAnimation", Animation.class);
        addRemapping("com.jme3.animation.SpatialAnimation", Animation.class);

        // Even though we no longer include Blender loading as part of the engine,
        // we leave this line in so that old j3os will still work and just not
        // load that data.  -pspeed:2020-04-19
        addRemapping("com.jme3.scene.plugins.blender.objects.Properties", NullSavable.class);
    }

    /**
     * A private constructor to inhibit instantiation of this class.
     */
    private SavableClassUtil() {
    }

    private static String remapClass(String className) throws ClassNotFoundException {
        String result = CLASS_REMAPPINGS.get(className);
        if (result == null) {
            return className;
        } else {
            return result;
        }
    }

    public static boolean isImplementingSavable(Class clazz) {
        boolean result = Savable.class.isAssignableFrom(clazz);
        return result;
    }

    @SuppressWarnings("unchecked")
    public static int[] getSavableVersions(Class clazz) throws IOException {
        ArrayList versionList = new ArrayList<>();
        Class superclass = clazz;
        do {
            versionList.add(getSavableVersion(superclass));
            superclass = superclass.getSuperclass();
        } while (superclass != null && SavableClassUtil.isImplementingSavable(superclass));

        int[] versions = new int[versionList.size()];
        for (int i = 0; i < versionList.size(); i++) {
            versions[i] = versionList.get(i);
        }
        return versions;
    }

    @SuppressWarnings("unchecked")
    public static int getSavableVersion(Class clazz) throws IOException {
        try {
            Field field = clazz.getField("SAVABLE_VERSION");
            Class declaringClass = (Class) field.getDeclaringClass();
            if (declaringClass == clazz) {
                return field.getInt(null);
            } else {
                return 0; // This class doesn't declare this field, e.g. version == 0
            }
        } catch (IllegalAccessException ex) {
            IOException ioEx = new IOException();
            ioEx.initCause(ex);
            throw ioEx;
        } catch (IllegalArgumentException ex) {
            throw ex; // can happen if SAVABLE_VERSION is not static
        } catch (NoSuchFieldException ex) {
            return 0; // not using versions
        }
    }

    public static int getSavedSavableVersion(Object savable,
            Class desiredClass, int[] versions, int formatVersion) {
        Class thisClass = savable.getClass();
        int count = 0;

        while (thisClass != desiredClass) {
            thisClass = thisClass.getSuperclass();
            if (thisClass != null && SavableClassUtil.isImplementingSavable(thisClass)) {
                count++;
            } else {
                break;
            }
        }

        if (thisClass == null) {
            throw new IllegalArgumentException(savable.getClass().getName() +
                                               " does not extend " +
                                               desiredClass.getName() + "!");
        } else if (count >= versions.length) {
            if (formatVersion <= 1) {
                return 0; // for buggy versions of j3o
            } else {
                throw new IllegalArgumentException(savable.getClass().getName() +
                                                   " cannot access version of " +
                                                   desiredClass.getName() +
                                                   " because it doesn't implement Savable");
            }
        }
        return versions[count];
    }

    /**
     * fromName creates a new Savable from the provided class name. First registered modules
     * are checked to handle special cases, if the modules do not handle the class name, the
     * class is instantiated directly.
     * @param className the class name to create.
     * @return the Savable instance of the class.
     * @throws InstantiationException thrown if the class does not have an empty constructor.
     * @throws IllegalAccessException thrown if the class is not accessible.
     * @throws InvocationTargetException if the underlying constructor throws an exception
     * @throws ClassNotFoundException thrown if the class name is not in the classpath.
     */
    public static Savable fromName(String className)
            throws ClassNotFoundException, IllegalAccessException,
            InstantiationException, InvocationTargetException {
        className = remapClass(className);

        Constructor noArgConstructor = findNoArgConstructor(className);
        noArgConstructor.setAccessible(true);
        try {
            return (Savable) noArgConstructor.newInstance();
        } catch (InvocationTargetException | InstantiationException e) {
            Logger.getLogger(SavableClassUtil.class.getName()).log(
                    Level.SEVERE, "Could not access constructor of class ''{0}" + "''! \n"
                    + "Some types need to have the BinaryImporter set up in a special way. Please double-check the setup.", className);
            throw e;
        } catch (IllegalAccessException e) {
            Logger.getLogger(SavableClassUtil.class.getName()).log(
                    Level.SEVERE, "{0} \n"
                    + "Some types need to have the BinaryImporter set up in a special way. Please double-check the setup.", e.getMessage());
            throw e;
        }
    }

    /**
     * @deprecated use {@link #fromName(java.lang.String)} instead 
     */
    @Deprecated
    public static Savable fromName(String className, List loaders) throws InstantiationException,
            InvocationTargetException, NoSuchMethodException,
            IllegalAccessException, ClassNotFoundException, IOException {
        if (loaders == null) {
            return fromName(className);
        }

        String newClassName = remapClass(className);
        synchronized (loaders) {
            for (ClassLoader classLoader : loaders) {
                final Class loadedClass;
                try {
                    loadedClass = classLoader.loadClass(newClassName);
                } catch (final ClassNotFoundException e) {
                    continue;
                }
                try {
                    return (Savable) loadedClass.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                }
            }
        }

        return fromName(className);
    }

    /**
     * Use reflection to gain access to the no-arg constructor of the named
     * class.
     *
     * @return the pre-existing constructor (not null)
     */
    @SuppressWarnings("unchecked")
    private static Constructor findNoArgConstructor(String className)
            throws ClassNotFoundException, InstantiationException {
        Class clazz = Class.forName(className);
        Constructor result;
        try {
            result = clazz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            throw new InstantiationException(
                    "Loading requires a no-arg constructor, but class "
                    + className + " lacks one.");
        }

        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy