private List<MethodMetadata> locateAccessors(
      final JavaType javaType,
      final MemberDetails memberDetails,
      final String metadataIdentificationString) {
    final SortedSet<MethodMetadata> locatedAccessors =
        new TreeSet<MethodMetadata>(
            new Comparator<MethodMetadata>() {
              public int compare(final MethodMetadata l, final MethodMetadata r) {
                return l.getMethodName().compareTo(r.getMethodName());
              }
            });

    for (MethodMetadata method : memberDetails.getMethods()) {
      // Exclude cyclic self-references (ROO-325)
      if (BeanInfoUtils.isAccessorMethod(method)
          && !method.getReturnType().equals(javaType)
          && !method.getMethodName().getSymbolName().equals("getDisplayString")) {
        locatedAccessors.add(method);
        // Track any changes to that method (eg it goes away)
        metadataDependencyRegistry.registerDependency(
            method.getDeclaredByMetadataId(), metadataIdentificationString);
      }
    }

    return new ArrayList<MethodMetadata>(locatedAccessors);
  }
  private List<Token> tokenize(
      MemberDetails memberDetails, JavaSymbolName finderName, String plural) {
    String simpleTypeName = getConcreteJavaType(memberDetails).getSimpleTypeName();
    String finder = finderName.getSymbolName();

    // Just in case it starts with findBy we can remove it here
    String findBy = "find" + plural + "By";
    if (finder.startsWith(findBy)) {
      finder = finder.substring(findBy.length());
    }

    // If finder still contains the findBy sequence it is most likely a wrong finder (ie someone
    // pasted the finder string accidentally twice
    if (finder.contains(findBy)) {
      throw new InvalidFinderException(
          "Dynamic finder definition for '"
              + finderName.getSymbolName()
              + "' in "
              + simpleTypeName
              + ".java is invalid");
    }

    SortedSet<FieldToken> fieldTokens = new TreeSet<FieldToken>();
    for (MethodMetadata methodMetadata : getLocatedMutators(memberDetails)) {
      FieldMetadata fieldMetadata =
          BeanInfoUtils.getFieldForPropertyName(
              memberDetails, methodMetadata.getParameterNames().get(0));

      // If we did find a field matching the first parameter name of the mutator method we can add
      // it to the finder ITD
      if (fieldMetadata != null) {
        fieldTokens.add(new FieldToken(fieldMetadata));
      }
    }

    List<Token> tokens = new ArrayList<Token>();

    while (finder.length() > 0) {
      Token token = getFirstToken(fieldTokens, finder, finderName.getSymbolName(), simpleTypeName);
      if (token != null) {
        if (token instanceof FieldToken || token instanceof ReservedToken) {
          tokens.add(token);
        }
        finder = finder.substring(token.getValue().length());
      }
    }

    return tokens;
  }
  public Set<JavaType> getPersistentFieldTypes(
      final JavaType thisType, final PersistenceMemberLocator persistenceMemberLocator) {
    final MethodMetadata identifierAccessor =
        persistenceMemberLocator.getIdentifierAccessor(thisType);
    final MethodMetadata versionAccessor = persistenceMemberLocator.getVersionAccessor(thisType);

    final Set<JavaType> fieldTypes = new LinkedHashSet<JavaType>();
    for (final MethodMetadata method : getMethods()) {
      // Not interested in non-accessor methods or persistence identifiers
      // and version fields
      if (!BeanInfoUtils.isAccessorMethod(method)
          || method.hasSameName(identifierAccessor, versionAccessor)) {
        continue;
      }

      // Not interested in fields that are JPA transient fields or
      // immutable fields
      final FieldMetadata field = BeanInfoUtils.getFieldForJavaBeanMethod(this, method);
      if (field == null
          || field.getCustomData().keySet().contains(CustomDataKeys.TRANSIENT_FIELD)
          || !BeanInfoUtils.hasAccessorAndMutator(field, this)) {
        continue;
      }
      final JavaType returnType = method.getReturnType();
      if (returnType.isCommonCollectionType()) {
        for (final JavaType genericType : returnType.getParameters()) {
          fieldTypes.add(genericType);
        }
      } else {
        if (!field.getCustomData().keySet().contains(EMBEDDED_FIELD)) {
          fieldTypes.add(returnType);
        }
      }
    }
    return fieldTypes;
  }
  public String buildUiXml(
      final String templateContents,
      final String destFile,
      final List<MethodMetadata> proxyMethods) {
    FileReader fileReader = null;
    try {
      final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
      builder.setEntityResolver(
          new EntityResolver() {
            public InputSource resolveEntity(final String publicId, final String systemId)
                throws SAXException, IOException {
              if (systemId.equals("http://dl.google.com/gwt/DTD/xhtml.ent")) {
                return new InputSource(
                    FileUtils.getInputStream(GwtScaffoldMetadata.class, "templates/xhtml.ent"));
              }

              // Use the default behaviour
              return null;
            }
          });

      InputSource source = new InputSource();
      source.setCharacterStream(new StringReader(templateContents));

      final Document templateDocument = builder.parse(source);

      if (!new File(destFile).exists()) {
        return transformXml(templateDocument);
      }

      source = new InputSource();
      fileReader = new FileReader(destFile);
      source.setCharacterStream(fileReader);
      final Document existingDocument = builder.parse(source);

      // Look for the element holder denoted by the 'debugId' attribute
      // first
      Element existingHoldingElement =
          XmlUtils.findFirstElement(
              "//*[@debugId='" + "boundElementHolder" + "']",
              existingDocument.getDocumentElement());
      Element templateHoldingElement =
          XmlUtils.findFirstElement(
              "//*[@debugId='" + "boundElementHolder" + "']",
              templateDocument.getDocumentElement());

      // If holding element isn't found then the holding element is either
      // not widget based or using the old convention of 'id' so look for
      // the element holder with an 'id' attribute
      if (existingHoldingElement == null) {
        existingHoldingElement =
            XmlUtils.findFirstElement(
                "//*[@id='" + "boundElementHolder" + "']", existingDocument.getDocumentElement());
      }
      if (templateHoldingElement == null) {
        templateHoldingElement =
            XmlUtils.findFirstElement(
                "//*[@id='" + "boundElementHolder" + "']", templateDocument.getDocumentElement());
      }

      if (existingHoldingElement != null) {
        final Map<String, Element> templateElementMap = new LinkedHashMap<String, Element>();
        for (final Element element : XmlUtils.findElements("//*[@id]", templateHoldingElement)) {
          templateElementMap.put(element.getAttribute("id"), element);
        }

        final Map<String, Element> existingElementMap = new LinkedHashMap<String, Element>();
        for (final Element element : XmlUtils.findElements("//*[@id]", existingHoldingElement)) {
          existingElementMap.put(element.getAttribute("id"), element);
        }

        if (existingElementMap.keySet().containsAll(templateElementMap.values())) {
          return transformXml(existingDocument);
        }

        final List<Element> elementsToAdd = new ArrayList<Element>();
        for (final Map.Entry<String, Element> entry : templateElementMap.entrySet()) {
          if (!existingElementMap.keySet().contains(entry.getKey())) {
            elementsToAdd.add(entry.getValue());
          }
        }

        final List<Element> elementsToRemove = new ArrayList<Element>();
        for (final Map.Entry<String, Element> entry : existingElementMap.entrySet()) {
          if (!templateElementMap.keySet().contains(entry.getKey())) {
            elementsToRemove.add(entry.getValue());
          }
        }

        for (final Element element : elementsToAdd) {
          final Node importedNode = existingDocument.importNode(element, true);
          existingHoldingElement.appendChild(importedNode);
        }

        for (final Element element : elementsToRemove) {
          existingHoldingElement.removeChild(element);
        }

        if (elementsToAdd.size() > 0) {
          final List<Element> sortedElements = new ArrayList<Element>();
          for (final MethodMetadata method : proxyMethods) {
            final String propertyName =
                StringUtils.uncapitalize(
                    BeanInfoUtils.getPropertyNameForJavaBeanMethod(method).getSymbolName());
            final Element element =
                XmlUtils.findFirstElement(
                    "//*[@id='" + propertyName + "']", existingHoldingElement);
            if (element != null) {
              sortedElements.add(element);
            }
          }
          for (final Element el : sortedElements) {
            if (el.getParentNode() != null && el.getParentNode().equals(existingHoldingElement)) {
              existingHoldingElement.removeChild(el);
            }
          }
          for (final Element el : sortedElements) {
            existingHoldingElement.appendChild(el);
          }
        }

        return transformXml(existingDocument);
      }

      return transformXml(templateDocument);
    } catch (final Exception e) {
      throw new IllegalStateException(e);
    } finally {
      IOUtils.closeQuietly(fileReader);
    }
  }
  /**
   * Obtains the "toString" method for this type, if available.
   *
   * <p>If the user provided a non-default name for "toString", that method will be returned.
   *
   * @return the "toString" method declared on this type or that will be introduced (or null if
   *     undeclared and not introduced)
   */
  public MethodMetadata getToStringMethod() {
    // Compute the relevant toString method name
    JavaSymbolName methodName = new JavaSymbolName("toString");
    if (!this.toStringMethod.equals("")) {
      methodName = new JavaSymbolName(this.toStringMethod);
    }

    // See if the type itself declared the method
    MethodMetadata result =
        MemberFindingUtils.getDeclaredMethod(governorTypeDetails, methodName, null);
    if (result != null) {
      return result;
    }

    // Decide whether we need to produce the toString method
    if (this.toStringMethod.equals("")) {
      return null;
    }

    InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    bodyBuilder.appendFormalLine("StringBuilder sb = new StringBuilder();");

    /** Key: field name, Value: accessor name */
    Map<String, String> map = new LinkedHashMap<String, String>();

    /** Field names */
    List<String> order = new ArrayList<String>();

    Set<String> excludeFieldsSet = new LinkedHashSet<String>();
    if (excludeFields != null && excludeFields.length > 0) {
      Collections.addAll(excludeFieldsSet, excludeFields);
    }

    for (MethodMetadata accessor : locatedAccessors) {
      String accessorName = accessor.getMethodName().getSymbolName();
      String fieldName = BeanInfoUtils.getPropertyNameForJavaBeanMethod(accessor).getSymbolName();
      if (!excludeFieldsSet.contains(StringUtils.uncapitalize(fieldName))
          && !map.containsKey(fieldName)) {
        String accessorText = accessorName + "()";
        if (accessor.getReturnType().isCommonCollectionType()) {
          accessorText = accessorName + "() == null ? \"null\" : " + accessorName + "().size()";
        } else if (accessor.getReturnType().isArray()) {
          accessorText = "java.util.Arrays.toString(" + accessorName + "())";
        } else if (Calendar.class
            .getName()
            .equals(accessor.getReturnType().getFullyQualifiedTypeName())) {
          accessorText = accessorName + "() == null ? \"null\" : " + accessorName + "().getTime()";
        }
        map.put(fieldName, accessorText);
        order.add(fieldName);
      }
    }

    if (!order.isEmpty()) {
      int index = 0;
      int size = map.keySet().size();
      for (String fieldName : order) {
        index++;
        String accessorText = map.get(fieldName);
        StringBuilder string = new StringBuilder();
        string
            .append("sb.append(\"")
            .append(fieldName)
            .append(": \").append(")
            .append(accessorText)
            .append(")");
        if (index < size) {
          string.append(".append(\", \")");
        }
        string.append(";");
        bodyBuilder.appendFormalLine(string.toString());
      }

      bodyBuilder.appendFormalLine("return sb.toString();");

      MethodMetadataBuilder methodBuilder =
          new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, STRING, bodyBuilder);
      result = methodBuilder.build();
    }

    return result;
  }