Esempio n. 1
0
 private void saveSearchFile() {
   if (mySearch != null) {
     try {
       FileWriter fwriter =
           new FileWriter(
               ScilabConstants.SCIHOME.toString() + File.separator + getPersistentId() + ".xml");
       BufferedWriter buffer = new BufferedWriter(fwriter);
       buffer.append("<SearchResults editor=\"" + editor.getUUID() + "\"");
       buffer.append(" base=\"" + mySearch.base + "\"");
       buffer.append(" recursive=\"" + mySearch.recursive + "\"");
       buffer.append(" ignoreCR=\"" + mySearch.ignoreCR + "\"");
       buffer.append(
           " filePattern=\"" + ScilabXMLUtilities.getXMLString(mySearch.filePattern) + "\"");
       buffer.append(" fileCaseSensitive=\"" + mySearch.fileCaseSensitive + "\"");
       if (mySearch.wordPattern != null && !mySearch.wordPattern.isEmpty()) {
         buffer.append(
             " wordPattern=\"" + ScilabXMLUtilities.getXMLString(mySearch.wordPattern) + "\"");
       }
       buffer.append(" wordCaseSensitive=\"" + mySearch.wordCaseSensitive + "\"");
       buffer.append(" wholeWord=\"" + mySearch.wholeWord + "\"");
       buffer.append(" regexp=\"" + mySearch.regexp + "\"");
       buffer.append(">\n");
       mySearch.getResults().toXML(buffer, 1);
       buffer.append("</SearchResults>");
       buffer.close();
     } catch (Exception e) {
       e.printStackTrace();
     }
   }
 }
Esempio n. 2
0
/**
 * Class to retrieve object from the xml configuration file
 *
 * @author Calixte DENIZET
 */
public class XConfiguration {

  // User configuration file
  private static final String USER_CONFIG_FILE =
      ScilabConstants.SCIHOME.toString() + "/XConfiguration.xml";
  private static final String SCI = System.getenv("SCI");
  private static final String SCILAB_CONFIG_FILE =
      SCI + "/modules/preferences/etc/XConfiguration.xml";

  private static final String ERROR_READ = Messages.gettext("Could not load file: ");
  private static final String ERROR_WRITE = Messages.gettext("Could not write the file: ");
  private static final String SEVERE_ERROR =
      Messages.gettext("A severe error occurred: cannot load the preferences file.");
  private static final String PARSING_ERROR =
      Messages.gettext(
          "An error occurred when loading the preferences file, try to reload the default one.");

  private static final XPathFactory xpathFactory = XPathFactory.newInstance();
  private static final Map<Class<?>, StringParser> conv = new HashMap<Class<?>, StringParser>();

  private static final EventListenerList listenerList = new EventListenerList();
  private static final Set<String> modifiedPaths = new HashSet<String>();

  private static Document doc;
  private static boolean hasBeenRead;

  static {
    addXConfigurationListener(ScilabGeneralPrefs.getInstance());

    try {
      Class histoprefs =
          ClassLoader.getSystemClassLoader()
              .loadClass("org.scilab.modules.history_manager.HistoryPrefs");
      Method getinstance = histoprefs.getDeclaredMethod("getInstance");
      addXConfigurationListener((XConfigurationListener) getinstance.invoke(null));
    } catch (ClassNotFoundException e) {
      // Nothing displayed (always occurs in MN mode)
    } catch (Exception e) {
      System.err.println(e);
    }
  }

  /**
   * Get the document in SCIHOME corresponding to the configuration file.
   *
   * @return the configuration document.
   */
  public static Document getXConfigurationDocument() {
    if (doc == null) {
      boolean error = false;
      File xml = new File(USER_CONFIG_FILE);
      if (!xml.exists()) {
        ScilabXMLUtilities.writeDocument(createDocument(), USER_CONFIG_FILE);
      }

      DocumentBuilder docBuilder = null;

      try {
        DocumentBuilderFactory factory = ScilabDocumentBuilderFactory.newInstance();
        docBuilder = factory.newDocumentBuilder();
        doc = docBuilder.parse(xml);
        float version = getDocumentVersion(doc);
        float defaultVersion = getDocumentVersion(getDefaultDocument());
        if (defaultVersion != version) {
          xml.delete();
          doc = null;
          return getXConfigurationDocument();
        } else {
          return doc;
        }
      } catch (ParserConfigurationException pce) {
        error = true;
      } catch (SAXException se) {
        error = true;
      } catch (IOException ioe) {
        error = true;
      }

      if (error) {
        if (hasBeenRead) {
          System.err.println(SEVERE_ERROR);
          doc = null;
          xml.delete();
          return docBuilder.newDocument();
        }

        hasBeenRead = true;
        doc = null;
        xml.delete();
        System.err.println(PARSING_ERROR);
        return getXConfigurationDocument();
      }

      return docBuilder.newDocument();
    }

    return doc;
  }

  /** Save the modifications */
  public static void writeDocument(String filename, Node written) {
    Transformer transformer = null;
    try {
      transformer = ScilabTransformerFactory.newInstance().newTransformer();
    } catch (TransformerConfigurationException e1) {
      System.err.println(ERROR_WRITE + filename);
      return;
    } catch (TransformerFactoryConfigurationError e1) {
      System.err.println(ERROR_WRITE + filename);
      return;
    }
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");

    StreamResult result = new StreamResult(new File(filename));
    DOMSource source = new DOMSource(written);
    try {
      transformer.transform(source, result);
    } catch (TransformerException e) {
      System.err.println(ERROR_WRITE + filename);
      return;
    }

    // Invalidate the current document
    if (filename.equals(USER_CONFIG_FILE)) {
      doc = null;
    }
  }

  /** Save the modifications */
  public static String dumpNode(Node written) {
    Transformer transformer = null;
    try {
      transformer = ScilabTransformerFactory.newInstance().newTransformer();
    } catch (TransformerConfigurationException e1) {
      System.err.println("Cannot dump xml");
      return "";
    } catch (TransformerFactoryConfigurationError e1) {
      System.err.println("Cannot dump xml");
      return "";
    }
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    StreamResult result = new StreamResult(new BufferedOutputStream(stream));
    DOMSource source = new DOMSource(written);
    String str = "";
    try {
      transformer.transform(source, result);
      str = stream.toString();
    } catch (TransformerException e) {
      System.err.println("Cannot dump xml");
      return str;
    } finally {
      try {
        stream.close();
      } catch (Exception e) {
      }
    }

    return str;
  }

  /**
   * Get the document version
   *
   * @param doc the document
   * @return the version
   */
  private static float getDocumentVersion(Document doc) {
    if (doc != null) {
      Element root = doc.getDocumentElement();
      String version = root.getAttribute("version");

      try {
        return Float.parseFloat(version);
      } catch (NumberFormatException e) {
      }
    }

    return 0.0f;
  }

  /**
   * Get the default document
   *
   * @return the document
   */
  private static Document getDefaultDocument() {
    DocumentBuilder docBuilder;
    DocumentBuilderFactory factory;
    Document mainDoc = null;

    try {
      factory = ScilabDocumentBuilderFactory.newInstance();
      docBuilder = factory.newDocumentBuilder();
      mainDoc = docBuilder.parse(SCILAB_CONFIG_FILE);
    } catch (ParserConfigurationException pce) {
      System.err.println("Cannot create a XML DocumentBuilder:\n" + pce);
      return null;
    } catch (SAXException se) {
      System.err.println("Weird... Cannot parse basic file:\n" + se);
      return null;
    } catch (IOException ioe) {
      System.err.println("Weird... Cannot parse basic file:\n" + ioe);
      return null;
    }

    return mainDoc;
  }

  /**
   * Create a document in using the XConfiguration-*.xml found in SCI/modules/MODULE_NAME/etc/
   *
   * @return the built document
   */
  public static Document createDocument() {
    DocumentBuilder docBuilder;
    DocumentBuilderFactory factory;
    Document mainDoc = getDefaultDocument();
    if (mainDoc == null) {
      return null;
    }

    Element root = mainDoc.getDocumentElement();

    factory = ScilabDocumentBuilderFactory.newInstance();

    try {
      docBuilder = factory.newDocumentBuilder();
    } catch (ParserConfigurationException pce) {
      System.err.println("Cannot create a XML DocumentBuilder:\n" + pce);
      return null;
    }

    List<File> etcs = getEtcDir();
    for (File etc : etcs) {
      File[] xmls =
          etc.listFiles(
              new FilenameFilter() {
                public boolean accept(File dir, String name) {
                  return name.endsWith(".xml") && name.startsWith("XConfiguration-");
                }
              });
      for (File xml : xmls) {
        try {
          Document doc = docBuilder.parse(xml);
          if (xml.getName().equals("XConfiguration-general.xml")) {
            try {
              XPath xp = xpathFactory.newXPath();
              NodeList nodes =
                  (NodeList)
                      xp.compile(
                              "//shortcuts/body/actions/action-folder/action[contains(@key, 'OSSCKEY')]")
                          .evaluate(doc, XPathConstants.NODESET);
              for (int i = 0; i < nodes.getLength(); i++) {
                Element e = (Element) nodes.item(i);
                e.setAttribute(
                    "key",
                    e.getAttribute("key").replace("OSSCKEY", ScilabKeyStroke.getOSMetaKey()));
              }
            } catch (XPathExpressionException e) {
              System.err.println(e);
            }
          }
          Node node = mainDoc.importNode(doc.getDocumentElement(), true);
          NodeList list = root.getElementsByTagName(node.getNodeName());
          if (list.getLength() != 0) {
            root.replaceChild(node, list.item(0));
          }
        } catch (SAXException se) {
          System.err.println(ERROR_READ + xml.getName());
        } catch (IOException ioe) {
          System.err.println(ERROR_READ + xml.getName());
        }
      }
    }

    return mainDoc;
  }

  /**
   * Get a the list of the etc dirs in modules dirs
   *
   * @return the lit of etc dirs
   */
  public static List<File> getEtcDir() {
    List<File> list = new ArrayList<File>();
    File modulesDir = new File(SCI + "/modules/");
    File[] modules =
        modulesDir.listFiles(
            new FileFilter() {
              public boolean accept(File f) {
                return f.isDirectory();
              }
            });

    for (File module : modules) {
      File etc = new File(module, "/etc/");
      if (etc.exists() && etc.isDirectory()) {
        list.add(etc);
      }
    }

    return list;
  }

  public static void addModifiedPath(String path) {
    if (path != null && !path.isEmpty()) {
      modifiedPaths.add(path);
    }
  }

  public static void invalidate() {
    modifiedPaths.clear();
    doc = null;
  }

  public static void addXConfigurationListener(XConfigurationListener listener) {
    listenerList.add(XConfigurationListener.class, listener);
  }

  public static void removeXConfigurationListener(XConfigurationListener listener) {
    listenerList.remove(XConfigurationListener.class, listener);
  }

  public static XConfigurationListener[] getXConfigurationListeners() {
    return listenerList.getListeners(XConfigurationListener.class);
  }

  public static void fireXConfigurationEvent() {
    if (!modifiedPaths.isEmpty()) {
      XConfigurationEvent event = null;
      Object[] listeners = listenerList.getListenerList();
      for (int i = listeners.length - 2; i >= 0; i -= 2) {
        if (listeners[i] == XConfigurationListener.class) {
          if (event == null) {
            event = new XConfigurationEvent(modifiedPaths);
          }
          ((XConfigurationListener) listeners[i + 1]).configurationChanged(event);
        }
      }

      modifiedPaths.clear();
    }
  }

  /**
   * Register a StringParser for a given Class
   *
   * @param type the class type
   * @param parser the StringParser
   */
  public static void registerStringParser(Class<?> type, StringParser parser) {
    conv.put(type, parser);
  }

  /**
   * Get a StringParser for a given Class
   *
   * @param type the class type
   * @return the corresponding parser
   */
  public static StringParser getStringParser(Class<?> type) {
    return conv.get(type);
  }

  public static void set(final Document doc, final String path, String value) {
    XPath xp = xpathFactory.newXPath();
    NodeList nodes;
    try {
      nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      System.err.println(e);
      return;
    }

    for (int i = 0; i < nodes.getLength(); i++) {
      Node n = nodes.item(i);
      if (n != null && n.getNodeType() == Node.ATTRIBUTE_NODE) {
        n.setNodeValue(value);
      }
    }

    writeDocument(USER_CONFIG_FILE, doc);
  }

  /** Save the current file */
  public static void save() {
    if (doc != null) {
      writeDocument(USER_CONFIG_FILE, doc);
    }
  }

  /**
   * Get all the nodes with the given path. All the get nodes are serialized into an object (generic
   * paramater) which must have a constructor without argument and with methods named
   * set&lt;Attribute Name&gt; with one argument and no returned value. For example a node &lt;foo
   * aaa="1" bbb="true" ccc-ddd="#001122"/&gt; could be retrieved with something like
   * XConfiguration.get(MyObject.class, doc, "//path/to/node") where MyObject should be something
   * like <code>
   * public class MyObject {
   *
   *    public MyObject() {
   *       // ...
   *    }
   *
   *    public void setAaa(int a) {
   *       // ...
   *    }
   *
   *    public void setBbb(boolean b) {
   *       // ...
   *    }
   *
   *    public void setCccDdd(Color c) {
   *       // ...
   *    }
   * }
   * </code> If an attribute must not be retrieved, just remove the setter.
   *
   * <p>It is possible to use the annotation @XConfAttribute to make easier the retrievement. For
   * example <code>
   * @XConfAttribute
   * public class MyObject {
   *
   *    public MyObject() {
   *       // ...
   *    }
   *
   *    @XConfAttribute(attributes={"aaa", "bbb", "ccc-ddd"})
   *    // the contents of aaa will be converted into int and passed as first argument
   *    // the contents of bbb will be converted into boolean and passed as second argument
   *    // the contents of ccc-ddd will be converted into Color and passed as third argument
   *    public void set(int n, boolean b, Color c) {
   *       // ...
   *    }
   *  }
   * </code>
   *
   * @param type the Class type to retrieve
   * @param doc the document to explore
   * @param path the xpath query to retrieve the corresponding nodeset.
   * @return an array of instance of class type.
   */
  public static final <T> T[] get(final Class<T> type, final Document doc, final String path) {
    XPath xp = xpathFactory.newXPath();
    NodeList nodes;
    try {
      nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      System.err.println(e);
      return (T[]) Array.newInstance(type, 0);
    }

    if (type.getAnnotation(XConfAttribute.class) != null) {
      T[] arr = get(type, nodes);
      if (arr != null) {
        return arr;
      }
    }

    Method[] meths = type.getDeclaredMethods();
    Map<String, Method> mapMethods = new HashMap<String, Method>();
    for (Method m : meths) {
      String name = m.getName();
      if (name.startsWith("set")
          && m.getParameterTypes().length == 1
          && m.getReturnType().equals(Void.TYPE)) {
        mapMethods.put(m.getName(), m);
        m.setAccessible(true);
      }
    }

    Map<String, String> names = new HashMap<String, String>();

    T[] values = (T[]) Array.newInstance(type, nodes.getLength());
    for (int i = 0; i < values.length; i++) {
      NamedNodeMap map = nodes.item(i).getAttributes();
      try {
        Constructor<T> constructor = type.getDeclaredConstructor(new Class[] {});
        constructor.setAccessible(true);
        values[i] = constructor.newInstance();
      } catch (InstantiationException e) {
        System.err.println(e);
        break;
      } catch (IllegalAccessException e) {
        System.err.println(e);
        break;
      } catch (NoSuchMethodException e) {
        System.err.println(e);
        break;
      } catch (InvocationTargetException e) {
        System.err.println(e.getTargetException());
      }

      for (int j = 0; j < map.getLength(); j++) {
        Node n = map.item(j);
        String name = n.getNodeName();
        String methName = names.get(name);
        if (methName == null) {
          StringBuilder buffer = new StringBuilder("set");
          String[] parts = name.split("-");
          for (String part : parts) {
            if (part != null && part.length() > 0) {
              buffer.append(part.substring(0, 1).toUpperCase());
              buffer.append(part.substring(1).toLowerCase());
            }
          }
          methName = buffer.toString();
          names.put(name, methName);
        }
        String value = n.getNodeValue();
        Method method = mapMethods.get(methName);
        if (method != null) {
          Class[] paramsClass = method.getParameterTypes();
          StringParser parser = conv.get(paramsClass[0]);
          if (parser != null) {
            Object[] params = new Object[] {parser.parse(value)};
            try {
              method.invoke(values[i], params);
            } catch (IllegalAccessException e) {
              System.err.println(e);
            } catch (IllegalArgumentException e) {
              System.err.println(e);
            } catch (InvocationTargetException e) {
              System.err.println(e.getTargetException());
            }
          }
        }
      }
    }

    return values;
  }

  /**
   * Get a Map with where the key is the converted value (according to keyType) of the attribute
   * named key and the value is given in the same way.
   *
   * @param doc the document to read
   * @param key the attribute name where its value will be converted and used as a key in the map
   * @param keyType the Class of the key
   * @param value the attribute name where its value will be converted and used as a value in the
   *     map
   * @param valueType the Class of the value
   * @return the corresponding map.
   */
  public static final <T, U> Map<T, U> get(
      final Document doc,
      final String key,
      final Class<T> keyType,
      final String value,
      final Class<U> valueType,
      final String path) {
    XPath xp = xpathFactory.newXPath();
    Map<T, U> map = new HashMap<T, U>();
    NodeList nodes;
    try {
      nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      System.err.println(e);
      return map;
    }

    int len = nodes.getLength();
    for (int i = 0; i < len; i++) {
      NamedNodeMap nmap = nodes.item(i).getAttributes();
      Node k = nmap.getNamedItem(key);
      Node v = nmap.getNamedItem(value);
      if (k == null || v == null) {
        return map;
      }

      String kk = k.getNodeValue();
      String vv = v.getNodeValue();

      StringParser convK = conv.get(keyType);
      StringParser convV = conv.get(valueType);
      if (convK == null || convV == null) {
        return map;
      }

      map.put((T) convK.parse(kk), (U) convV.parse(vv));
    }

    return map;
  }

  /**
   * Getter for annoted class (with @XConfAttribute)
   *
   * @param type the class type
   * @param nodes the nodes to read
   * @return an array of instances of type, if the class is annoted
   *     with @XConfAttribute(isStatic=true), the returned array is empty.
   */
  private static final <T> T[] get(final Class<T> type, NodeList nodes) {
    Method[] meths = type.getDeclaredMethods();
    List<String[]> attrs = new ArrayList<String[]>();
    List<Method> methods = new ArrayList<Method>();
    Map<String[], Method> mapMethods = new HashMap<String[], Method>();
    for (Method m : meths) {
      String name = m.getName();
      Annotation ann = m.getAnnotation(XConfAttribute.class);
      if (ann != null) {
        String[] attributes = ((XConfAttribute) ann).attributes();
        if (attributes.length == m.getParameterTypes().length) {
          m.setAccessible(true);
          attrs.add(attributes);
          methods.add(m);
        } else {
          return null;
        }
      }
    }

    Annotation ann = type.getAnnotation(XConfAttribute.class);
    boolean mustInstantiate = !((XConfAttribute) ann).isStatic();

    T[] values = null;
    int len = nodes.getLength();
    if (mustInstantiate) {
      values = (T[]) Array.newInstance(type, len);
    }

    for (int i = 0; i < len; i++) {
      NamedNodeMap map = nodes.item(i).getAttributes();
      String nodeName = nodes.item(i).getNodeName();

      if (mustInstantiate) {
        try {
          Constructor<T> constructor = type.getDeclaredConstructor(new Class[] {});
          constructor.setAccessible(true);
          values[i] = constructor.newInstance();
        } catch (InstantiationException e) {
          System.err.println(e);
          break;
        } catch (IllegalAccessException e) {
          System.err.println(e);
          break;
        } catch (NoSuchMethodException e) {
          System.err.println(e);
          break;
        } catch (InvocationTargetException e) {
          System.err.println(e.getTargetException());
        }
      }

      for (int j = 0; j < methods.size(); j++) {
        Method method = methods.get(j);
        ann = method.getAnnotation(XConfAttribute.class);
        String tag = ((XConfAttribute) ann).tag();
        if (tag.isEmpty() || tag.equals(nodeName)) {
          String[] attributes = attrs.get(j);
          Object[] params = new Object[attributes.length];
          Class[] paramsClass = method.getParameterTypes();
          for (int k = 0; k < attributes.length; k++) {
            String p = "";
            Node node = null;
            if (map != null) {
              node = map.getNamedItem(attributes[k]);
            }
            if (node != null) {
              p = node.getNodeValue();
            }

            StringParser parser = conv.get(paramsClass[k]);
            if (parser != null) {
              params[k] = parser.parse(p);
            }
          }

          try {
            if (mustInstantiate) {
              method.invoke(values[i], params);
            } else {
              method.invoke(null, params);
            }
          } catch (IllegalAccessException e) {
            System.err.println(e);
          } catch (IllegalArgumentException e) {
            System.err.println(e);
          } catch (InvocationTargetException e) {
            System.err.println(e.getTargetException());
          }
        }
      }
    }

    if (mustInstantiate) {
      return values;
    } else {
      return (T[]) Array.newInstance(type, 0);
    }
  }

  /** Interface to implement to parse an attribute String into a Java object */
  public static interface StringParser {

    /**
     * Parse a string
     *
     * @param str the string to parse
     * @return the corresponding Object
     */
    public Object parse(String str);
  }

  static {
    conv.put(
        int.class,
        new StringParser() {
          public Integer parse(String str) {
            try {
              return Integer.parseInt(str);
            } catch (NumberFormatException e) {
              try {
                return (int) Double.parseDouble(str);
              } catch (NumberFormatException ee) {
                return new Integer(0);
              }
            }
          }
        });
    conv.put(
        char.class,
        new StringParser() {
          public Object parse(String str) {
            if (str.length() > 0) {
              return str.charAt(0);
            } else {
              return new Character((char) 0);
            }
          }
        });
    conv.put(
        byte.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Byte.parseByte(str);
            } catch (NumberFormatException e) {
              try {
                return (byte) Double.parseDouble(str);
              } catch (NumberFormatException ee) {
                return new Byte((byte) 0);
              }
            }
          }
        });
    conv.put(
        short.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Short.parseShort(str);
            } catch (NumberFormatException e) {
              try {
                return (short) Double.parseDouble(str);
              } catch (NumberFormatException ee) {
                return new Short((short) 0);
              }
            }
          }
        });
    conv.put(
        double.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Double.parseDouble(str);
            } catch (NumberFormatException ee) {
              return new Double((double) 0);
            }
          }
        });
    conv.put(
        float.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Float.parseFloat(str);
            } catch (NumberFormatException ee) {
              return new Float((float) 0);
            }
          }
        });
    conv.put(
        boolean.class,
        new StringParser() {
          public Object parse(String str) {
            return Boolean.parseBoolean(str);
          }
        });
    conv.put(
        long.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Long.parseLong(str);
            } catch (NumberFormatException e) {
              try {
                return (long) Double.parseDouble(str);
              } catch (NumberFormatException ee) {
                return new Long((long) 0);
              }
            }
          }
        });
    conv.put(
        String.class,
        new StringParser() {
          public Object parse(String str) {
            return str;
          }
        });
    conv.put(
        Color.class,
        new StringParser() {
          public Object parse(String str) {
            try {
              return Color.decode(str);
            } catch (NumberFormatException e) {
              return Color.BLACK;
            }
          }
        });
    conv.put(
        KeyStroke.class,
        new StringParser() {
          public Object parse(String str) {
            String[] toks = str.split(" +");
            StringBuilder buffer = new StringBuilder();
            for (int i = 0; i < toks.length - 1; i++) {
              buffer.append(toks[i].toLowerCase());
              buffer.append(" ");
            }
            buffer.append(toks[toks.length - 1].toUpperCase());
            return KeyStroke.getKeyStroke(buffer.toString());
          }
        });
  }

  @Retention(RetentionPolicy.RUNTIME)
  public @interface XConfAttribute {

    /**
     * Map method arguments with attributes name For example, <code>
     * @XConfAttribute(tag="mytag", attributes={"a", "b"})
     * void foo(String one, int tow) { ... }
     * </code> The value of attribute "a" is converted into a String and passed as "one"
     * argument,...
     */
    public String[] attributes() default {""};

    public String tag() default "";

    /** Used to avoid object instanciation so the differents annotated methods must be static. */
    public boolean isStatic() default false;
  }
}