예제 #1
0
  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");
  }
예제 #2
0
  /** 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);
  }
예제 #3
0
 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;
 }
예제 #4
0
  /** 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;
  }
예제 #5
0
    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;
    }
예제 #6
0
  /**
   * 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);
    }
  }
예제 #7
0
  /**
   * 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;
  }
예제 #8
0
  /**
   * 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.");
    }
  }
예제 #9
0
 /** 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);
 }
예제 #10
0
 /** Checks if the given object is created from this {@link Descriptor}. */
 public final boolean isInstance(T instance) {
   return clazz.isInstance(instance);
 }
예제 #11
0
 /**
  * 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();
 }
예제 #12
0
 public Enum[] getEnumConstants() {
   return (Enum[]) clazz.getEnumConstants();
 }