001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.metamodel.specloader.classsubstitutor;
021
022import java.util.Set;
023
024import com.google.common.collect.Sets;
025
026import org.apache.isis.applib.DomainObjectContainer;
027import org.apache.isis.core.commons.lang.ClassUtil;
028
029public abstract class ClassSubstitutorAbstract implements ClassSubstitutor {
030
031    private final Set<Class<?>> classesToIgnore = Sets.newHashSet();
032    private final Set<String> classNamesToIgnore = Sets.newHashSet();
033
034    /**
035     * Will implicitly ignore the {@link DomainObjectContainer}.
036     */
037    public ClassSubstitutorAbstract() {
038        ignore(DomainObjectContainer.class);
039        
040        // ignore cglib
041        ignore("net.sf.cglib.proxy.Factory");
042        ignore("net.sf.cglib.proxy.MethodProxy");
043        ignore("net.sf.cglib.proxy.Callback");
044
045        // ignore javassist
046        ignore("javassist.util.proxy.ProxyObject");
047        ignore("javassist.util.proxy.MethodHandler");
048
049    }
050
051    // /////////////////////////////////////////////////////////////////
052    // init, shutdown
053    // /////////////////////////////////////////////////////////////////
054
055    /**
056     * Default implementation does nothing.
057     */
058    @Override
059    public void init() {
060    }
061
062    /**
063     * Default implementation does nothing.
064     */
065    @Override
066    public void shutdown() {
067    }
068
069    // /////////////////////////////////////////////////////////////////
070    // ClassSubstitutor impl.
071    // /////////////////////////////////////////////////////////////////
072
073    /**
074     * Hook method for subclasses to override if required.
075     * 
076     * <p>
077     * Default implementation will either return the class, unless has been
078     * registered as to be {@link #ignore(Class) ignore}d, in which case returns
079     * <tt>null</tt>.
080     */
081    @Override
082    public Class<?> getClass(final Class<?> cls) {
083        if (shouldIgnore(cls)) {
084            return null;
085        }
086        final Class<?> superclass = cls.getSuperclass();
087        if(superclass != null && superclass.isEnum()) {
088            return superclass;
089        }
090        if (ClassUtil.directlyImplements(cls, JavassistEnhanced.class)) {
091            return getClass(cls.getSuperclass());
092        }
093        return cls;
094    }
095
096    
097    private boolean shouldIgnore(final Class<?> cls) {
098        if (cls.isArray()) {
099            return shouldIgnore(cls.getComponentType());
100        }
101        return classesToIgnore.contains(cls) || classNamesToIgnore.contains(cls.getCanonicalName());
102    }
103
104    // ////////////////////////////////////////////////////////////////////
105    // ignoring
106    // ////////////////////////////////////////////////////////////////////
107
108    /**
109     * For any classes registered as ignored, {@link #getClass(Class)} will
110     * return <tt>null</tt>.
111     */
112    protected boolean ignore(final Class<?> q) {
113        return classesToIgnore.add(q);
114    }
115
116    /**
117     * For any classes registered as ignored, {@link #getClass(Class)} will
118     * return <tt>null</tt>.
119     */
120    protected boolean ignore(final String className) {
121        return classNamesToIgnore.add(className);
122    }
123
124
125    // ////////////////////////////////////////////////////////////////////
126    // injectInto
127    // ////////////////////////////////////////////////////////////////////
128
129    @Override
130    public void injectInto(final Object candidate) {
131        if (ClassSubstitutorAware.class.isAssignableFrom(candidate.getClass())) {
132            final ClassSubstitutorAware cast = ClassSubstitutorAware.class.cast(candidate);
133            cast.setClassSubstitutor(this);
134        }
135    }
136
137}