private InputStream getHelpStream(Class c, String suffix) { Locale locale = Stapler.getCurrentRequest().getLocale(); String base = c.getName().replace('.', '/').replace('$', '/') + "/help" + suffix; ClassLoader cl = c.getClassLoader(); if (cl == null) return null; InputStream in; in = cl.getResourceAsStream( base + '_' + locale.getLanguage() + '_' + locale.getCountry() + '_' + locale.getVariant() + ".html"); if (in != null) return in; in = cl.getResourceAsStream( base + '_' + locale.getLanguage() + '_' + locale.getCountry() + ".html"); if (in != null) return in; in = cl.getResourceAsStream(base + '_' + locale.getLanguage() + ".html"); if (in != null) return in; // default return cl.getResourceAsStream(base + ".html"); }
/** Serves <tt>help.html</tt> from the resource of {@link #clazz}. */ public void doHelp(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { String path = req.getRestOfPath(); if (path.contains("..")) throw new ServletException("Illegal path: " + path); path = path.replace('/', '-'); for (Class c = clazz; c != null; c = c.getSuperclass()) { RequestDispatcher rd = Stapler.getCurrentRequest().getView(c, "help" + path); if (rd != null) { // Jelly-generated help page rd.forward(req, rsp); return; } InputStream in = getHelpStream(c, path); if (in != null) { // TODO: generalize macro expansion and perhaps even support JEXL rsp.setContentType("text/html;charset=UTF-8"); String literal = IOUtils.toString(in, "UTF-8"); rsp.getWriter() .println( Util.replaceMacro( literal, Collections.singletonMap("rootURL", req.getContextPath()))); in.close(); return; } } rsp.sendError(SC_NOT_FOUND); }
private String getViewPage(Class<?> clazz, String pageName, String defaultValue) { while (clazz != Object.class) { String name = clazz.getName().replace('.', '/').replace('$', '/') + "/" + pageName; if (clazz.getClassLoader().getResource(name) != null) return '/' + name; clazz = clazz.getSuperclass(); } return defaultValue; }
/** Given the class, list up its {@link PropertyType}s from its public fields/getters. */ private Map<String, PropertyType> buildPropertyTypes(Class<?> clazz) { Map<String, PropertyType> r = new HashMap<String, PropertyType>(); for (Field f : clazz.getFields()) r.put(f.getName(), new PropertyType(f)); for (Method m : clazz.getMethods()) if (m.getName().startsWith("get")) r.put(Introspector.decapitalize(m.getName().substring(3)), new PropertyType(m)); return r; }
private Class computeItemType() { if (clazz.isArray()) { return clazz.getComponentType(); } if (Collection.class.isAssignableFrom(clazz)) { Type col = Types.getBaseClass(type, Collection.class); if (col instanceof ParameterizedType) return Types.erasure(Types.getTypeArgument(col, 0)); else return Object.class; } return null; }
/** * Creates a configured instance from the submitted form. * * <p>Hudson only invokes this method when the user wants an instance of <tt>T</tt>. So there's no * need to check that in the implementation. * * <p>Starting 1.206, the default implementation of this method does the following: * * <pre> * req.bindJSON(clazz,formData); * </pre> * * <p>... which performs the databinding on the constructor of {@link #clazz}. * * <p>For some types of {@link Describable}, such as {@link ListViewColumn}, this method can be * invoked with null request object for historical reason. Such design is considered broken, but * due to the compatibility reasons we cannot fix it. Because of this, the default implementation * gracefully handles null request, but the contract of the method still is "request is always * non-null." Extension points that need to define the "default instance" semantics should define * a descriptor subtype and add the no-arg newInstance method. * * @param req Always non-null (see note above.) This object includes represents the entire * submission. * @param formData The JSON object that captures the configuration data for this {@link * Descriptor}. See http://wiki.hudson-ci.org/display/HUDSON/Structured+Form+Submission Always * non-null. * @throws FormException Signals a problem in the submitted form. * @since 1.145 */ public T newInstance(StaplerRequest req, JSONObject formData) throws FormException { try { Method m = getClass().getMethod("newInstance", StaplerRequest.class); if (!Modifier.isAbstract(m.getDeclaringClass().getModifiers())) { // this class overrides newInstance(StaplerRequest). // maintain the backward compatible behavior return verifyNewInstance(newInstance(req)); } else { if (req == null) { // yes, req is supposed to be always non-null, but see the note above return verifyNewInstance(clazz.newInstance()); } // new behavior as of 1.206 return verifyNewInstance(req.bindJSON(clazz, formData)); } } catch (NoSuchMethodException e) { throw new AssertionError(e); // impossible } catch (InstantiationException e) { throw new Error("Failed to instantiate " + clazz + " from " + formData, e); } catch (IllegalAccessException e) { throw new Error("Failed to instantiate " + clazz + " from " + formData, e); } catch (RuntimeException e) { throw new RuntimeException("Failed to instantiate " + clazz + " from " + formData, e); } }
/** * Returns the path to the help screen HTML for the given field. * * <p>The help files are assumed to be at "help/FIELDNAME.html" with possible locale variations. */ public String getHelpFile(final String fieldName) { for (Class c = clazz; c != null; c = c.getSuperclass()) { String page = "/descriptor/" + getId() + "/help"; String suffix; if (fieldName == null) { suffix = ""; } else { page += '/' + fieldName; suffix = '-' + fieldName; } try { if (Stapler.getCurrentRequest().getView(c, "help" + suffix) != null) return page; } catch (IOException e) { throw new Error(e); } InputStream in = getHelpStream(c, suffix); IOUtils.closeQuietly(in); if (in != null) return page; } return null; }
/** * Infers the type of the corresponding {@link Describable} from the outer class. This version * works when you follow the common convention, where a descriptor is written as the static nested * class of the describable class. * * @since 1.278 */ protected Descriptor() { this.clazz = (Class<T>) getClass().getEnclosingClass(); if (clazz == null) throw new AssertionError( getClass() + " doesn't have an outer class. Use the constructor that takes the Class object explicitly."); // detect an type error Type bt = Types.getBaseClass(getClass(), Descriptor.class); if (bt instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) bt; // this 't' is the closest approximation of T of Descriptor<T>. Class t = Types.erasure(pt.getActualTypeArguments()[0]); if (!t.isAssignableFrom(clazz)) throw new AssertionError( "Outer class " + clazz + " of " + getClass() + " is not assignable to " + t + ". Perhaps wrong outer class?"); } // detect a type error. this Descriptor is supposed to be returned from getDescriptor(), so make // sure its type match up. // this prevents a bug like // http://www.nabble.com/Creating-a-new-parameter-Type-%3A-Masked-Parameter-td24786554.html try { Method getd = clazz.getMethod("getDescriptor"); if (!getd.getReturnType().isAssignableFrom(getClass())) { throw new AssertionError(getClass() + " must be assignable to " + getd.getReturnType()); } } catch (NoSuchMethodException e) { throw new AssertionError(getClass() + " is missing getDescriptor method."); } }
/** Checks if the type represented by this descriptor is a subtype of the given type. */ public final boolean isSubTypeOf(Class type) { return type.isAssignableFrom(clazz); }
/** Checks if the given object is created from this {@link Descriptor}. */ public final boolean isInstance(T instance) { return clazz.isInstance(instance); }
/** * Uniquely identifies this {@link Descriptor} among all the other {@link Descriptor}s. * * <p>Historically {@link #clazz} is assumed to be unique, so this method uses that as the * default, but if you are adding {@link Descriptor}s programmatically for the same type, you can * change this to disambiguate them. * * @return Stick to valid Java identifier character, plus '.', which had to be allowed for * historical reasons. * @since 1.391 */ public String getId() { return clazz.getName(); }
public Enum[] getEnumConstants() { return (Enum[]) clazz.getEnumConstants(); }