001package org.kohsuke.stapler.lang;
002
003import org.kohsuke.stapler.Function;
004
005import java.net.URL;
006import java.util.ArrayList;
007import java.util.Collections;
008import java.util.LinkedHashMap;
009import java.util.List;
010import java.util.Map;
011import javax.annotation.Nonnull;
012
013/**
014 * Abstraction of class-like object, agnostic to languages.
015 *
016 * <p>
017 * To support other JVM languages that use their own specific types to represent a class
018 * (such as JRuby and Jython), we now use this object instead of {@link Class}. This allows
019 * us to reuse much of the logic of class traversal/resource lookup across different languages.
020 * 
021 * This is a convenient tuple so that we can pass around a single argument instead of two.
022 *
023 * @param <C>
024 *     Variable that represents the type of {@code Class} like object in this language.
025 *
026 * @author Kohsuke Kawaguchi
027 */
028public final class Klass<C> {
029    public final C clazz;
030    public final KlassNavigator<C> navigator;
031
032    public Klass(C clazz, KlassNavigator<C> navigator) {
033        this.clazz = clazz;
034        this.navigator = navigator;
035    }
036
037    public URL getResource(String resourceName) {
038        return navigator.getResource(clazz,resourceName);
039    }
040
041    public Iterable<Klass<?>> getAncestors() {
042        return navigator.getAncestors(clazz);
043    }
044    
045    public Klass<?> getSuperClass() {
046        return navigator.getSuperClass(clazz);
047    }
048
049    public Class toJavaClass() {
050        return navigator.toJavaClass(clazz);
051    }
052
053    /**
054     * @since 1.220
055     */
056    public List<MethodRef> getDeclaredMethods() {
057        return navigator.getDeclaredMethods(clazz);
058    }
059
060    /**
061     * Gets list of fields declared by the class.
062     * @return List of fields. 
063     *         May return empty list in the case of obsolete {@link #navigator}, which does not offer the method.
064     * @since 1.246
065     */
066    @Nonnull
067    public List<FieldRef> getDeclaredFields() {
068        return navigator.getDeclaredFields(clazz);
069    }
070
071    /**
072     * Gets all the public fields defined in this type, including super types.
073     *
074     * @see Class#getFields()
075     */
076    public List<FieldRef> getFields() {
077        Map<String,FieldRef> fields = new LinkedHashMap<String,FieldRef>();
078        for (Klass<?> k = this; k!=null; k=k.getSuperClass()) {
079            for (FieldRef f : k.getDeclaredFields()) {
080                String name = f.getName();
081                if (!fields.containsKey(name) && f.isRoutable()) {
082                    fields.put(name,f);
083                }
084            }
085        }
086
087        return new ArrayList<FieldRef>(fields.values());
088    }
089
090    /**
091     * Reports all the methods that can be used for routing requests on this class.
092     * @return List of functions. 
093     *         May return empty list in the case of obsolete {@link #navigator}, which does not offer the method.
094     * @since 1.246
095     */
096    @Nonnull
097    public List<Function> getFunctions() {
098        return navigator.getFunctions(clazz);
099    }
100
101    public boolean isArray() {
102        return navigator.isArray(clazz);
103    }
104
105    public Object getArrayElement(Object o, int index) throws IndexOutOfBoundsException {
106        return navigator.getArrayElement(o,index);
107    }
108
109    public boolean isMap() {
110        return navigator.isMap(clazz);
111    }
112
113    public Object getMapElement(Object o, String key) {
114        return navigator.getMapElement(o,key);
115    }
116
117    @Override
118    public boolean equals(Object o) {
119        if (this == o) return true;
120        if (o == null || getClass() != o.getClass()) return false;
121
122        Klass that = (Klass) o;
123        return clazz.equals(that.clazz) && navigator.equals(that.navigator);
124
125    }
126
127    @Override
128    public int hashCode() {
129        return 31 * clazz.hashCode() + navigator.hashCode();
130    }
131
132    @Override
133    public String toString() {
134        return clazz.toString();
135    }
136
137    /**
138     * Creates {@link Klass} from a Java {@link Class}.
139     */
140    public static Klass<Class> java(Class c) {
141        return c == null ? null : new Klass<Class>(c, KlassNavigator.JAVA);
142    }
143}