示例#1
0
  public void testWithCustomObjectAndEditorSelected() throws Exception {
    final PropertyEditor floatEditor = new SimpleFloatEditor();
    floatEditor.setValue(new Float("12.34"));
    String selectName = "testBean.someNumber";
    BindStatus bindStatus =
        new BindStatus(getRequestContext(), selectName, false) {
          @Override
          public PropertyEditor getEditor() {
            return floatEditor;
          }
        };
    getPageContext().setAttribute(SelectTag.LIST_VALUE_PAGE_ATTRIBUTE, bindStatus);

    this.tag.setValue("${myNumber}");
    this.tag.setLabel("${myNumber}");

    int result = this.tag.doStartTag();
    assertEquals(BodyTag.EVAL_BODY_BUFFERED, result);
    result = this.tag.doEndTag();
    assertEquals(Tag.EVAL_PAGE, result);

    String output = getOutput();
    assertOptionTagOpened(output);
    assertOptionTagClosed(output);
    assertContainsAttribute(output, "selected", "selected");
    assertBlockTagContains(output, "12.34f");
  }
示例#2
0
  /**
   * Set the object to be edited.
   *
   * @param value The object to be edited.
   */
  public void setObject(Object value) {
    if (!(_type.isInstance(value))) {
      throw new IllegalArgumentException(value.getClass() + " is not of type " + _type);
    }
    _value = value;

    // Disable event generation.
    _squelchChangeEvents = true;

    // Iterate over each property, doing a lookup on the associated editor
    // and setting the editor's value to the value of the property.
    Iterator it = _prop2Editor.keySet().iterator();
    while (it.hasNext()) {
      PropertyDescriptor desc = (PropertyDescriptor) it.next();
      PropertyEditor editor = (PropertyEditor) _prop2Editor.get(desc);
      Method reader = desc.getReadMethod();
      if (reader != null) {
        try {
          Object val = reader.invoke(_value, null);
          editor.setValue(val);
        } catch (IllegalAccessException ex) {
          ex.printStackTrace();
        } catch (InvocationTargetException ex) {
          ex.getTargetException().printStackTrace();
        }
      }
    }

    // Enable event generation.
    _squelchChangeEvents = false;
  }
示例#3
0
  public void testWithPropertyEditorStringComparison() throws Exception {
    final PropertyEditor testBeanEditor = new TestBeanPropertyEditor();
    testBeanEditor.setValue(new TestBean("Sally"));
    String selectName = "testBean.spouse";
    BindStatus bindStatus =
        new BindStatus(getRequestContext(), selectName, false) {
          @Override
          public PropertyEditor getEditor() {
            return testBeanEditor;
          }
        };
    getPageContext().setAttribute(SelectTag.LIST_VALUE_PAGE_ATTRIBUTE, bindStatus);

    this.tag.setValue("Sally");

    int result = this.tag.doStartTag();
    assertEquals(BodyTag.EVAL_BODY_BUFFERED, result);
    result = this.tag.doEndTag();
    assertEquals(Tag.EVAL_PAGE, result);

    String output = getOutput();
    assertOptionTagOpened(output);
    assertOptionTagClosed(output);
    assertContainsAttribute(output, "value", "Sally");
    assertContainsAttribute(output, "selected", "selected");
    assertBlockTagContains(output, "Sally");
  }
  /**
   * Convert the value to the required type (if necessary from a String), using the given property
   * editor.
   *
   * @param oldValue the previous value, if available (may be {@code null})
   * @param newValue the proposed new value
   * @param requiredType the type we must convert to (or {@code null} if not known, for example in
   *     case of a collection element)
   * @param editor the PropertyEditor to use
   * @return the new value, possibly the result of type conversion
   * @throws IllegalArgumentException if type conversion failed
   */
  private Object doConvertValue(
      Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
    Object convertedValue = newValue;

    if (editor != null && !(convertedValue instanceof String)) {
      // Not a String -> use PropertyEditor's setValue.
      // With standard PropertyEditors, this will return the very same object;
      // we just want to allow special PropertyEditors to override setValue
      // for type conversion from non-String values to the required type.
      try {
        editor.setValue(convertedValue);
        Object newConvertedValue = editor.getValue();
        if (newConvertedValue != convertedValue) {
          convertedValue = newConvertedValue;
          // Reset PropertyEditor: It already did a proper conversion.
          // Don't use it again for a setAsText call.
          editor = null;
        }
      } catch (Exception ex) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call",
              ex);
        }
        // Swallow and proceed.
      }
    }

    Object returnValue = convertedValue;

    if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
      // Convert String array to a comma-separated String.
      // Only applies if no PropertyEditor converted the String array before.
      // The CSV String will be passed into a PropertyEditor's setAsText method, if any.
      if (logger.isTraceEnabled()) {
        logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
      }
      convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
    }

    if (convertedValue instanceof String) {
      if (editor != null) {
        // Use PropertyEditor's setAsText in case of a String value.
        if (logger.isTraceEnabled()) {
          logger.trace(
              "Converting String to [" + requiredType + "] using property editor [" + editor + "]");
        }
        String newTextValue = (String) convertedValue;
        return doConvertTextValue(oldValue, newTextValue, editor);
      } else if (String.class == requiredType) {
        returnValue = convertedValue;
      }
    }

    return returnValue;
  }
 @SuppressWarnings("unchecked")
 protected <T> T convertValue(@Nullable Object value, @Nonnull Class<T> type) {
   if (value != null) {
     if (type.isAssignableFrom(value.getClass())) {
       return (T) value;
     } else {
       PropertyEditor editor = findEditor(type);
       editor.setValue(value);
       return (T) editor.getValue();
     }
   }
   return null;
 }
 /**
  * Convert the given text value using the given property editor.
  *
  * @param oldValue the previous value, if available (may be <code>null</code>)
  * @param newTextValue the proposed text value
  * @param editor the PropertyEditor to use
  * @return the converted value
  */
 protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
   try {
     editor.setValue(oldValue);
   } catch (Exception ex) {
     if (logger.isDebugEnabled()) {
       logger.debug(
           "PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call",
           ex);
     }
     // Swallow and proceed.
   }
   editor.setAsText(newTextValue);
   return editor.getValue();
 }
  public Element getElementServiceBindingValue(ServiceBinding binding, Element input) {
    if (input == null) throw new IllegalArgumentException("input cannot be null");

    PropertyEditor editor = PropertyEditorFinder.getInstance().find(Element.class);
    if (editor == null)
      throw new IllegalStateException("Cannot find PropertyEditor for type Element");

    editor.setValue(input);
    Reader reader = new StringReader(editor.getAsText());
    Writer writer = new StringWriter();

    doXslTransform(binding, getConfig(binding), reader, writer);

    editor.setAsText(writer.toString());
    return (Element) editor.getValue();
  }
示例#8
0
  /**
   * 将对象中的属性值置入到fields中。
   *
   * <p>对于<code>isValidated()</code>为<code>true</code>的group,该方法无效。
   */
  public void mapTo(Object object) {
    if (isValidated() || object == null) {
      return;
    }

    if (log.isDebugEnabled()) {
      log.debug(
          "Mapping properties to fields: group=\"{}\", object={}",
          getName(),
          ObjectUtil.identityToString(object));
    }

    BeanWrapper bean = new BeanWrapperImpl(object);
    getForm().getFormConfig().getPropertyEditorRegistrar().registerCustomEditors(bean);

    for (Field field : getFields()) {
      String propertyName = field.getFieldConfig().getPropertyName();

      if (bean.isReadableProperty(propertyName)) {
        Object propertyValue = bean.getPropertyValue(propertyName);
        Class<?> propertyType = bean.getPropertyType(propertyName);
        PropertyEditor editor = bean.findCustomEditor(propertyType, propertyName);

        if (editor == null) {
          editor = BeanUtils.findEditorByConvention(propertyType);
        }

        if (editor == null) {
          if (propertyType.isArray()
              || CollectionFactory.isApproximableCollectionType(propertyType)) {
            field.setValues((String[]) bean.convertIfNecessary(propertyValue, String[].class));
          } else {
            field.setValue(bean.convertIfNecessary(propertyValue, String.class));
          }
        } else {
          editor.setValue(propertyValue);
          field.setValue(editor.getAsText());
        }
      } else {
        log.debug(
            "No readable property \"{}\" found in type {}",
            propertyName,
            object.getClass().getName());
      }
    }
  }
示例#9
0
 @Override
 public void actionPerformed(java.awt.event.ActionEvent e) {
   try {
     PropertyEditor propEd = property.getPropertyEditor();
     propEd.setValue(property.getValue());
     final Component custEditor = propEd.getCustomEditor();
     Object[] options = buttons();
     DialogDescriptor descriptor =
         new DialogDescriptor(
             custEditor,
             (String) getValue(Action.NAME),
             true,
             options,
             DialogDescriptor.CANCEL_OPTION,
             DialogDescriptor.DEFAULT_ALIGN,
             HelpCtx.DEFAULT_HELP,
             (ActionEvent e1) -> {
               try {
                 String action = e1.getActionCommand();
                 switch (action) {
                   case OK_COMMAND:
                     Object value = property.getPropertyEditor().getValue();
                     property.setValue(value);
                     break;
                   case RESTORE_COMMAND:
                     property.restoreDefaultValue();
                     break;
                 }
                 dialog.dispose();
               } catch (Exception ex) {
                 NotifyDescriptor descriptor2 =
                     new NotifyDescriptor.Message(
                         NbBundle.getMessage(PropertyAction.class, "MSG_InvalidValue")); // NOI18N
                 DialogDisplayer.getDefault().notify(descriptor2);
               }
             });
     descriptor.setClosingOptions(new Object[0]);
     dialog = DialogDisplayer.getDefault().createDialog(descriptor);
     dialog.setVisible(true);
     dialog = null;
   } catch (Exception ex) {
     ErrorManager.getDefault().notify(ex);
   }
 }
示例#10
0
  @Override
  protected final int doStartTagInternal() throws JspException {
    if (this.value != null) {
      // Find the containing EditorAwareTag (e.g. BindTag), if applicable.
      EditorAwareTag tag =
          (EditorAwareTag) TagSupport.findAncestorWithClass(this, EditorAwareTag.class);
      if (tag == null) {
        throw new JspException(
            "TransformTag can only be used within EditorAwareTag (e.g. BindTag)");
      }

      // OK, let's obtain the editor...
      String result = null;
      PropertyEditor editor = tag.getEditor();
      if (editor != null) {
        // If an editor was found, edit the value.
        editor.setValue(this.value);
        result = editor.getAsText();
      } else {
        // Else, just do a toString.
        result = this.value.toString();
      }
      result = htmlEscape(result);
      if (this.var != null) {
        pageContext.setAttribute(this.var, result, TagUtils.getScope(this.scope));
      } else {
        try {
          // Else, just print it out.
          pageContext.getOut().print(result);
        } catch (IOException ex) {
          throw new JspException(ex);
        }
      }
    }

    return SKIP_BODY;
  }
示例#11
0
  protected void getBeanElementProperties(Element element, Object bean, PropertyDescriptor pd)
      throws IntrospectionException, IllegalAccessException {
    Element propertyElement = null;
    Class classOfProperty = pd.getPropertyType();
    Object[] argsNone = {};
    // If the property is "class" and the type is java.lang.Class.class then
    // this is the class of the bean, which we've already encoded.
    // In this special case, return null.
    if (!((pd.getName().charAt(0) == 'c') && pd.getName().equals("class"))
        && !classOfProperty.equals(java.lang.Class.class)) {
      // Don't process property if it is in the list of fields to be ignored
      boolean omittedField = false;
      try {
        Field field = bean.getClass().getDeclaredField(pd.getName());
        omittedField = field.isAnnotationPresent(ObjectXmlOmitField.class);
      } catch (NoSuchFieldException nsfe) {
      }
      if (!omittedField) {
        String propertyName = formatName(pd.getName());
        // Hereafter, we're trying to create a representation of the property
        // based on the property's value.
        // The very first thing we need to do is get the value of the
        // property as an object. If we can't do that, we can't get any
        // representation of the property at all.
        Object propertyValue = null;
        try {
          Method getter = pd.getReadMethod();
          if (getter != null) {
            propertyValue = getter.invoke(bean, argsNone);
          }
        } catch (Exception ex) {
          // couldn't get value
          System.err.println(ex.getMessage());
        }
        // See if this property's value is something we can represent as a
        // standard data type that can be converted to an xsd type,
        // or if it's something that must be represented as a JavaBean.
        if (propertyElement == null) {

          PropertyEditor propEditor = PropertyEditorManager.findEditor(classOfProperty);
          // If the property editor is not null, pass the property's
          // value to the PropertyEditor, and then ask the PropertyEditor
          // for the object and then attempt to convert it to a standard type.

          if ((propEditor != null)
              || (classOfProperty == java.util.Calendar.class)
              || (classOfProperty == java.util.Date.class)) {
            // The object is a standard type
            // (e.g. a wrapper around a primitive type, a String, or a Date or Calendar object)
            try {
              Object object;
              if ((classOfProperty == java.util.Calendar.class)
                  || (classOfProperty == java.util.Date.class)) object = propertyValue;
              else {
                propEditor.setValue(propertyValue);
                object = propEditor.getValue();
              }
              Element childElement = getStandardObjectElement(document, object, propertyName);
              if (includeNullValues
                  || !childElement
                      .getAttribute(
                          NamespaceConstants.NSPREFIX_SCHEMA_XSI
                              + ":"
                              + org.apache.axis.Constants.ATTR_TYPE)
                      .equals("anyType")) {
                if (!includeTypeInfo) {
                  childElement.removeAttribute(
                      NamespaceConstants.NSPREFIX_SCHEMA_XSI
                          + ":"
                          + org.apache.axis.Constants.ATTR_TYPE);
                  childElement.removeAttribute(NamespaceConstants.NSPREFIX_SCHEMA_XSI + ":null");
                }
                try {
                  Field field = bean.getClass().getDeclaredField(propertyName);
                  if (field.isAnnotationPresent(ObjectXmlAsAttribute.class)) {
                    String attrName = null;
                    if (includeTypeInfo) attrName = nsPrefix + ":" + propertyName;
                    else attrName = propertyName;
                    element.setAttribute(attrName, childElement.getFirstChild().getNodeValue());
                  } else if (field.isAnnotationPresent(ObjectXmlAsValue.class))
                    element.setTextContent(childElement.getFirstChild().getNodeValue());
                  else element.appendChild(childElement);
                } catch (NoSuchFieldException nfse) {
                  element.appendChild(childElement);
                }
              }
            } catch (Exception e) {
            }
          } else {
            // The object is not an XSD-encoded datatype, it must be a JavaBean
            try {
              String propertyTypeName = null;
              if (propertyValue != null) {
                // get object type
                Class propertyType = pd.getPropertyType();
                propertyTypeName = propertyType.getName();
              }
              getBeanElements(element, propertyName, propertyTypeName, propertyValue);
            } catch (Exception e) {
            }
          }
        }
      }
    }
  }
示例#12
0
  public Reference createReference(Object bean) throws NamingException {
    try {
      BeanInfo bi = Introspector.getBeanInfo(bean.getClass());
      PropertyDescriptor[] pds = bi.getPropertyDescriptors();
      List refAddrs = new ArrayList();
      String factoryClassLocation = defaultFactoryClassLocation;

      boolean using_ref_props = referenceProperties.size() > 0;

      // we only include this so that on dereference we are not surprised to find some properties
      // missing
      if (using_ref_props)
        refAddrs.add(
            new BinaryRefAddr(REF_PROPS_KEY, SerializableUtils.toByteArray(referenceProperties)));

      for (int i = 0, len = pds.length; i < len; ++i) {
        PropertyDescriptor pd = pds[i];
        String propertyName = pd.getName();
        // System.err.println("Making Reference: " + propertyName);

        if (using_ref_props && !referenceProperties.contains(propertyName)) {
          // System.err.println("Not a ref_prop -- continuing.");
          continue;
        }

        Class propertyType = pd.getPropertyType();
        Method getter = pd.getReadMethod();
        Method setter = pd.getWriteMethod();
        if (getter != null
            && setter != null) // only use properties that are both readable and writable
        {
          Object val = getter.invoke(bean, EMPTY_ARGS);
          // System.err.println( "val: " + val );
          if (propertyName.equals("factoryClassLocation")) {
            if (String.class != propertyType)
              throw new NamingException(
                  this.getClass().getName()
                      + " requires a factoryClassLocation property to be a string, "
                      + propertyType.getName()
                      + " is not valid.");
            factoryClassLocation = (String) val;
          }

          if (val == null) {
            RefAddr addMe = new BinaryRefAddr(propertyName, NULL_TOKEN_BYTES);
            refAddrs.add(addMe);
          } else if (Coerce.canCoerce(propertyType)) {
            RefAddr addMe = new StringRefAddr(propertyName, String.valueOf(val));
            refAddrs.add(addMe);
          } else // other Object properties
          {
            RefAddr addMe = null;
            PropertyEditor pe = BeansUtils.findPropertyEditor(pd);
            if (pe != null) {
              pe.setValue(val);
              String textValue = pe.getAsText();
              if (textValue != null) addMe = new StringRefAddr(propertyName, textValue);
            }
            if (addMe == null) // property editor approach failed
            addMe =
                  new BinaryRefAddr(
                      propertyName,
                      SerializableUtils.toByteArray(
                          val, indirector, IndirectPolicy.INDIRECT_ON_EXCEPTION));
            refAddrs.add(addMe);
          }
        } else {
          // 				System.err.println(this.getClass().getName() +
          // 						   ": Skipping " + propertyName + " because it is " + (setter == null ?
          // "read-only." : "write-only."));

          if (logger.isLoggable(MLevel.WARNING))
            logger.warning(
                this.getClass().getName()
                    + ": Skipping "
                    + propertyName
                    + " because it is "
                    + (setter == null ? "read-only." : "write-only."));
        }
      }
      Reference out =
          new Reference(bean.getClass().getName(), factoryClassName, factoryClassLocation);
      for (Iterator ii = refAddrs.iterator(); ii.hasNext(); ) out.add((RefAddr) ii.next());
      return out;
    } catch (Exception e) {
      // e.printStackTrace();
      if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
        logger.log(MLevel.FINE, "Exception trying to create Reference.", e);

      throw new NamingException("Could not create reference from bean: " + e.toString());
    }
  }
  /**
   * Convert the value to the required type (if necessary from a String), for the specified
   * property.
   *
   * @param propertyName name of the property
   * @param oldValue previous value, if available (may be <code>null</code>)
   * @param newValue proposed change value
   * @param requiredType the type we must convert to (or <code>null</code> if not known, for example
   *     in case of a collection element)
   * @return the new value, possibly the result of type conversion
   * @throws TypeMismatchException if type conversion failed
   */
  protected Object doTypeConversionIfNecessary(
      String propertyName,
      String fullPropertyName,
      Object oldValue,
      Object newValue,
      Class requiredType)
      throws TypeMismatchException {

    Object convertedValue = newValue;
    if (convertedValue != null) {

      // Custom editor for this type?
      PropertyEditor pe = findCustomEditor(requiredType, fullPropertyName);

      // Value not of required type?
      if (pe != null
          || (requiredType != null
              && (requiredType.isArray()
                  || !requiredType.isAssignableFrom(convertedValue.getClass())))) {

        if (requiredType != null) {
          if (pe == null) {
            // No custom editor -> check BeanWrapperImpl's default editors.
            pe = (PropertyEditor) getDefaultEditor(requiredType);
            if (pe == null) {
              // No BeanWrapper default editor -> check standard JavaBean editors.
              pe = PropertyEditorManager.findEditor(requiredType);
            }
          }
        }

        if (pe != null && !(convertedValue instanceof String)) {
          // Not a String -> use PropertyEditor's setValue.
          // With standard PropertyEditors, this will return the very same object;
          // we just want to allow special PropertyEditors to override setValue
          // for type conversion from non-String values to the required type.
          try {
            pe.setValue(convertedValue);
            Object newConvertedValue = pe.getValue();
            if (newConvertedValue != convertedValue) {
              convertedValue = newConvertedValue;
              // Reset PropertyEditor: It already did a proper conversion.
              // Don't use it again for a setAsText call.
              pe = null;
            }
          } catch (IllegalArgumentException ex) {
            throw new TypeMismatchException(
                createPropertyChangeEvent(fullPropertyName, oldValue, newValue), requiredType, ex);
          }
        }

        if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
          // Convert String array to a comma-separated String.
          // Only applies if no PropertyEditor converted the String array before.
          // The CSV String will be passed into a PropertyEditor's setAsText method, if any.
          if (logger.isDebugEnabled()) {
            logger.debug(
                "Converting String array to comma-delimited String [" + convertedValue + "]");
          }
          convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
        }

        if (pe != null && convertedValue instanceof String) {
          // Use PropertyEditor's setAsText in case of a String value.
          if (logger.isDebugEnabled()) {
            logger.debug(
                "Converting String to [" + requiredType + "] using property editor [" + pe + "]");
          }
          try {
            pe.setValue(oldValue);
            pe.setAsText((String) convertedValue);
            convertedValue = pe.getValue();
          } catch (IllegalArgumentException ex) {
            throw new TypeMismatchException(
                createPropertyChangeEvent(fullPropertyName, oldValue, newValue), requiredType, ex);
          }
        }

        if (requiredType != null) {
          // Array required -> apply appropriate conversion of elements.
          if (requiredType.isArray()) {
            Class componentType = requiredType.getComponentType();
            if (convertedValue instanceof Collection) {
              // Convert Collection elements to array elements.
              Collection coll = (Collection) convertedValue;
              Object result = Array.newInstance(componentType, coll.size());
              int i = 0;
              for (Iterator it = coll.iterator(); it.hasNext(); i++) {
                Object value =
                    doTypeConversionIfNecessary(
                        propertyName,
                        propertyName + PROPERTY_KEY_PREFIX + i + PROPERTY_KEY_SUFFIX,
                        null,
                        it.next(),
                        componentType);
                Array.set(result, i, value);
              }
              return result;
            } else if (convertedValue != null && convertedValue.getClass().isArray()) {
              // Convert Collection elements to array elements.
              int arrayLength = Array.getLength(convertedValue);
              Object result = Array.newInstance(componentType, arrayLength);
              for (int i = 0; i < arrayLength; i++) {
                Object value =
                    doTypeConversionIfNecessary(
                        propertyName,
                        propertyName + PROPERTY_KEY_PREFIX + i + PROPERTY_KEY_SUFFIX,
                        null,
                        Array.get(convertedValue, i),
                        componentType);
                Array.set(result, i, value);
              }
              return result;
            } else {
              // A plain value: convert it to an array with a single component.
              Object result = Array.newInstance(componentType, 1);
              Object value =
                  doTypeConversionIfNecessary(
                      propertyName,
                      propertyName + PROPERTY_KEY_PREFIX + 0 + PROPERTY_KEY_SUFFIX,
                      null,
                      convertedValue,
                      componentType);
              Array.set(result, 0, value);
              return result;
            }
          }

          // If the resulting value definitely doesn't match the required type,
          // try field lookup as fallback. If no matching field found,
          // throw explicit TypeMismatchException with full context information.
          if (convertedValue != null
              && !requiredType.isPrimitive()
              && !requiredType.isAssignableFrom(convertedValue.getClass())) {

            // In case of String value, try to find matching field (for JDK 1.5
            // enum or custom enum with values defined as static fields).
            if (convertedValue instanceof String) {
              try {
                Field enumField = requiredType.getField((String) convertedValue);
                return enumField.get(null);
              } catch (Exception ex) {
                logger.debug("Field [" + convertedValue + "] isn't an enum value", ex);
              }
            }

            // Definitely doesn't match: throw TypeMismatchException.
            throw new TypeMismatchException(
                createPropertyChangeEvent(fullPropertyName, oldValue, newValue), requiredType);
          }
        }
      }
    }

    return convertedValue;
  }
示例#14
0
  @SuppressWarnings("rawtypes")
  private static MenuManager generateListEditorFor(
      final IMenuManager manager,
      final MenuManager subMenu,
      final PropertyDescriptor thisP,
      final Editable[] editables,
      final Layers theLayers,
      final Layer topLevelLayer) {

    // find out the type of the editor
    final Method m = thisP.getReadMethod();
    final Class cl = m.getReturnType();
    MenuManager result = subMenu;

    // is there a custom editor for this type?
    final Class c = thisP.getPropertyEditorClass();

    PropertyEditor pe = null;
    // try to create an editor for this class
    try {
      if (c != null) pe = (PropertyEditor) c.newInstance();
    } catch (final Exception e) {
      MWC.Utilities.Errors.Trace.trace(e);
    }

    // did it work?
    if (pe == null) {
      // try to find an editor for this through our manager
      pe = PropertyEditorManager.findEditor(cl);
    }

    // retrieve the tags
    final String[] tags = pe.getTags();

    // are there any tags for this class?
    if (tags != null) {
      // create a drop-down list
      final MenuManager thisChoice = new MenuManager(thisP.getDisplayName());

      // sort out the setter details
      final Method getter = thisP.getReadMethod();

      // get the current value
      Object val = null;
      try {
        val = getter.invoke(editables[0], (Object[]) null);
      } catch (final Exception e) {
        MWC.Utilities.Errors.Trace.trace(e);
      }
      pe.setValue(val);

      // convert the current value to text
      final String currentValue = pe.getAsText();

      // and now a drop-down item for each options
      for (int j = 0; j < tags.length; j++) {
        final String thisTag = tags[j];
        pe.setAsText(thisTag);
        final Object thisValue = pe.getValue();

        // create the item
        final IAction thisA =
            new Action(thisTag, IAction.AS_RADIO_BUTTON) {
              public void run() {
                try {
                  // hey, since this is a radio button, we get two events when the
                  // selection changes - one for the value being unset, and the
                  // other
                  // for the value being set. So just fire for the new value (the
                  // one that's checked)
                  if (isChecked()) {
                    final Method setter = thisP.getWriteMethod();

                    // ok, place the change in the action
                    final ListPropertyAction la =
                        new ListPropertyAction(
                            thisP.getDisplayName(),
                            editables,
                            getter,
                            setter,
                            thisValue,
                            theLayers,
                            topLevelLayer);

                    // and add it to the history
                    CorePlugin.run(la);
                  }
                } catch (final Exception e) {
                  CorePlugin.logError(
                      IStatus.INFO, "While executing select editor for:" + thisP, e);
                }
              }
            };

        // is this the current one?
        if (thisTag.equals(currentValue)) {
          thisA.setChecked(true);
        }

        // add it to the menu
        thisChoice.add(thisA);
      }

      // is our sub-menu already created?
      if (result == null) {
        String nameStr;
        if (editables.length > 1) nameStr = MULTIPLE_ITEMS_STR;
        else nameStr = editables[0].getName();

        result = new MenuManager(nameStr);
        manager.add(result);
      }

      result.add(thisChoice);
    }

    return result;
  }
 public void propertyChange(PropertyChangeEvent evt) {
   if (PropertyEnv.PROP_STATE.equals(evt.getPropertyName())
       && evt.getNewValue() == PropertyEnv.STATE_VALID) {
     editor.setValue(getPropertyValue());
   }
 }