/* Jaxe - Editeur XML en Java Copyright (C) 2002 Observatoire de Paris-Meudon Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure. Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU . Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis. */ package jaxe; import java.awt.Toolkit; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.StringTokenizer; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.KeyStroke; import javax.swing.text.Position; import org.apache.xerces.parsers.DOMParser; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.xml.sax.InputSource; import textbender.recombinant.common.lang.ClassLoaderX; /* 3 regular expression matching libraries have been tested. code is left as comment since it could be reused - jakarta-regexp (1.2) quickly gets StackOverflowException, and generates RESyntaxException: Syntax error: Closure operand can't be nullable - jakarta-oro-awk (2.0.8) is the fastest but a bit big; limited to awk regular expressions; limited to 8-bit ASCII - gnu.regexp (1.1.4) is a bit slow */ //jakarta-regexp //import org.apache.regexp.RE; //import org.apache.regexp.RESyntaxException; //jakarta-oro import org.apache.oro.text.regex.*; import org.apache.oro.text.awk.*; //gnu.regexp //import gnu.regexp.*; /** * Gestion du fichier de configuration et du fichier de schéma XML */ public class Config { public static final String ELEM_FICHER_TITRES = "FICHIERTITRES"; static String newline = Jaxe.newline; public Element jaxecfg; public Element schema; public Hashtable fichierXSL2Parametres ; String schemaNamespace; public String targetNamespace; // private File cfgdir; // public File schemaFile; String namespacecfg; String prefixecfg; // liste des éléments avec un attribut name (en général sous xs:schema), avec les inclusions éventuelles // (Element ArrayList) ArrayList lelements; ArrayList lgroups; ArrayList lcomptypes; ArrayList lsimptypes; ArrayList lattgroups; ArrayList lextensions; ArrayList nomsElements; // String ArrayList synchro avec lelements // liste de tous les éléments (pas forcément directement sous xs:schema) ArrayList ltouselements; ArrayList ltousgroups; ArrayList ltousextensions; Hashtable cacheBaliseDef; // cache des associations nombalise -> définition Hashtable cacheNomsBalises; // cache des associations définition -> nombalise Hashtable cacheInsertion = null; // cache des expressions régulières pour les insertions Hashtable cacheSubst; // cache pour ajSubst Hashtable cacheParametres = null; Hashtable hashPrefixes = null; // associations espaces de noms -> préfixes Hashtable hashVerif = null; // associations éléments de définition d'éléments ou d'attributs -> VerifTypeSimple ArrayList cacheListeEspace = null; // liste d'espaces de noms, y compris dans les sous-configs // private String schemadir = null; // répertoire du schéma principal ArrayList fichiersInclus; ArrayList autresConfigs; // jakarta-oro PatternCompiler compiler; PatternMatcher matcher; ResourceBundle resourceTitres; public Config(String nomFichierCfg, boolean lireSchema) { super(); if (nomFichierCfg == null) { jaxecfg = null; return; } fichierXSL2Parametres = new Hashtable() ; // jakarta-oro compiler = new AwkCompiler(); matcher = new AwkMatcher(); Document configdoc; try { DOMParser parser = new DOMParser(); InputStream stream = getClass().getClassLoader().getResourceAsStream(nomFichierCfg); ClassLoaderX.checked(nomFichierCfg,stream); InputSource source = new InputSource(stream); parser.parse(source); configdoc = parser.getDocument(); } catch (Exception e) { // IOException, MissingResourceException, SAXException e.printStackTrace(System.err); return; } jaxecfg = configdoc.getDocumentElement(); // cfgdir = (new File(nomFichierCfg)).getParentFile(); autresConfigs = new ArrayList(); NodeList lconfig = jaxecfg.getElementsByTagName("CONFIG"); for (int i=0; i 0) { JMenu menu = mbar.getMenu(0); mbar.remove(menu); barreBalises.add(menu); } } return(barreBalises); } public String description() { String desc = null; if (resourceTitres != null) { try { desc = resourceTitres.getString("description_config"); } catch (MissingResourceException ex) { } } if (desc == null) { NodeList nl = jaxecfg.getElementsByTagName("DESCRIPTION"); if (nl == null || nl.getLength() == 0) return(null); Element descel = (Element)nl.item(0); if (descel.getFirstChild() == null) return(null); desc = descel.getFirstChild().getNodeValue().trim(); } return(desc); } public Element racine() { NodeList lracine = jaxecfg.getElementsByTagName("RACINE"); if (lracine.getLength() == 0) return(null); Element racine = (Element)lracine.item(0); NodeList nl = racine.getElementsByTagName("BALISE"); Element balise = (Element)nl.item(0); return(balise); } /** * Retourne la liste des noms des éléments racines possibles */ public ArrayList listeRacines() { ArrayList liste = new ArrayList(); NodeList lracine = jaxecfg.getElementsByTagName("RACINE"); for (int i=0; i 0) liste.add(nomBalise((Element)lbalise.item(0))); } return(liste); } public String chercherNamespace() { NodeList nl = jaxecfg.getElementsByTagName("ESPACE"); if (nl == null || nl.getLength() == 0) return(null); Element espace = (Element)nl.item(0); String uri = espace.getAttribute("uri"); if ("".equals(uri)) uri = null; return(uri); } /** * Renvoit l'espace de nom donné dans le fichier de config, ou null si aucun n'est défini */ public String namespace() { return(namespacecfg); } public String chercherPrefixe() { NodeList nl = jaxecfg.getElementsByTagName("ESPACE"); if (nl == null || nl.getLength() == 0) return(null); Element espace = (Element)nl.item(0); String pref = espace.getAttribute("prefixe"); if ("".equals(pref)) pref = null; return(pref); } /** * Renvoit le préfixe donné dans le fichier de config, ou null si aucun n'est défini */ public String prefixe() { return(prefixecfg); } /** * Renvoit l'espace de nom correspondant à la définition de l'élément dans le fichier de config, * un String vide si targetNamespace est vide, ou null si aucun schéma XML n'est utilisé et que * l'espace de noms n'est pas défini. */ public String espaceElement(Element balisedef) { Config conf = getDefConf(balisedef); if (conf != this) return(conf.espaceElement(balisedef)); if (schema != null) { Element sbalisedef = schemaBaliseDef(nomBalise(balisedef)); if (sbalisedef == null) System.err.println("erreur: balise inconnue dans le schéma: " + nomBalise(balisedef)); Element schemael = sbalisedef.getOwnerDocument().getDocumentElement(); return(schemael.getAttribute("targetNamespace")); } else { return(namespace()); } } /** * Renvoit le préfixe à utiliser pour créer un élément dont on donne l'élément le définissant dans la config, * ou null s'il n'y en a pas. */ public String prefixeElement(Element balisedef) { Config conf = getDefConf(balisedef); if (conf != this) return(conf.prefixeElement(balisedef)); String espace = espaceElement(balisedef); if (espace == null) return(null); return((String)hashPrefixes.get(espace)); } /** * Renvoit la liste des espaces de noms (String) gérés par cette config et ses sous-configs. */ protected ArrayList listeEspaces() { if (cacheListeEspace != null) return(cacheListeEspace); ArrayList liste = new ArrayList(); if (namespacecfg == null) liste.add(""); else if (!hashPrefixes.containsKey(namespacecfg)) liste.add(namespacecfg); Enumeration espaces = hashPrefixes.keys(); for (; espaces.hasMoreElements() ;) { String s = (String)espaces.nextElement(); if (liste.indexOf(s) == -1) liste.add(s); } for (int i=0; i 0) { Config conf = getBaliseConf(nombalise); if (conf != null) return(conf.schemaBaliseDef(nombalise)); } return(null); } /** * Renvoit la config correspondant à un nom d'élément. * Attention: peut être ambiguë si le nom n'a pas de préfixe. * Il est donc préférable d'utiliser getDefConf et getElementConf à la place. */ public Config getBaliseConf(String nombalise) { if (autresConfigs.size() == 0) return(this); int inds = nombalise.indexOf(':'); if (inds != -1) { String prefixe = nombalise.substring(0, inds); for (int i=0; i (a*|b?)?c? * (a?|b?)* -> (a|b)* * (a?b?c?)* -> (a|b|c)* (modechoice=true) * ((a?b*c*)|d?)? -> (((ab*c*)|(b+c*)|(b*c+))|d)? (modepasnul=true) * ((a|b)*|c)* -> (a|b|c)* * on pourrait simplifier et retirer modepasnul si on continue * d'utiliser jakarta-oro au lieu de jakarta-regexp */ protected String expressionReguliere(Element sparent, int niveau, boolean modechoice, boolean modevisu, boolean modepasnul, int imodepasnul, boolean modevalid) { //System.out.println("expressionReguliere " + sparent.getNodeName() + " " + niveau + // " modechoice=" + modechoice + " modevisu=" + modevisu + " modepasnul=" + // modepasnul + " " + imodepasnul + " " + modevalid); String regexp = null; String nombalise = sparent.getLocalName(); if (niveau == 1 && nombalise.equals("element") && !"".equals(sparent.getAttribute("type"))) { String stype = localValue(sparent.getAttribute("type")); for (int i=0; i on autorise tout String min = sparent.getAttribute("minOccurs"); String max = sparent.getAttribute("maxOccurs"); if (!"".equals(max) && !"1".equals(max)) { if ("0".equals(min)) regexp = ".*"; else regexp = ".+"; } else regexp = "[^_]+_"; } else if (nombalise.equals("all")) { // impossible de faire une expression régulière correspondante => on autorise (a|b|...|z)+ boolean nouveaumodechoice = !modevisu && !modepasnul; NodeList lsousb = sparent.getChildNodes(); for (int i=0; i " + regexp); return(regexp); } /** * Expression régulière correspondant au schéma pour un élément parent donné */ public String expressionReguliere(Element parentdef) { if (schema == null) { ArrayList lsousb = listeSousbalises(parentdef); String expr = ""; for (int i=0; i= pos.getOffset(); JaxeElement jcadet = null; if (danslazone) jcadet = parent.enfantApres(pos.getOffset()); String cettexp = null; boolean insere = false; NodeList lsousb = parent.noeud.getChildNodes(); for (int i=0; i= pos.getOffset()) { insere = true; danslazone = false; } } } } } if (!insere && danslazone) { if (cettexp == null) cettexp = nomBalise(aInserer) + "_"; else cettexp += nomBalise(aInserer) + "_"; } return(cettexp); } /** * renvoit true si on peut insérer l'élement aInsérer sous la balise parent à la position pos. */ public boolean insertionPossible (JaxeElement parent, Position pos, Element aInserer) { //System.out.println("insertionPossible " + namespacecfg + " " + parent.noeud.getNodeName() + " " + // nomBalise(aInserer)); if (schema == null) return(true); // on suppose que le test de sous-balise a déjà été fait if (autresConfigs.size() > 0) { Config conf = getDefConf(aInserer); Config pconf = getElementConf((Element)parent.noeud); if (conf != pconf) { Element noeudparent = chercheParentEspace((Element)parent.noeud, conf.namespace()); if (noeudparent == null) return(true); parent = parent.doc.getElementForNode(noeudparent); if (conf != this) //return(conf.insertionPossible(parent, pos, aInserer)); return(true); } else { if (conf != this) //return(conf.insertionPossible(parent, pos, aInserer)); return(true); /* pb: on ne peut pas tester l'ordre des éléments dans certains cas, par exemple: xxx yyy Ici on autorise deux éléments title sous head alors qu'un seul est normalement autorisé. Par contre on peut tester les imbrications (title est autorisé sous head). */ } } Element sparent = schemaBaliseDef(localValue(parent.noeud.getNodeName())); String espaceRacine = parent.noeud.getOwnerDocument().getDocumentElement().getNamespaceURI(); boolean xslt = "http://www.w3.org/1999/XSL/Transform".equals(espaceRacine); String cettexp = expressionEspace(parent, pos, aInserer, xslt); //System.out.println("cettexp: " + cettexp); if (cacheInsertion == null) cacheInsertion = new Hashtable(); // jakarta-regexp //RE r = (RE)cacheInsertion.get(sparent); // jakarta-oro Pattern r = (Pattern)cacheInsertion.get(sparent); // gnu-regexp //RE r = (RE)cacheInsertion.get(sparent); if (r == null) { String expr = "^" + expressionReguliere(sparent, 1, false, false, false, 0, false) + "$"; /* // jakarta-regexp try { r = new RE(expr); } catch (RESyntaxException ex) { System.err.println("RESyntaxException: " + ex.getMessage()); System.err.println(expr); return(true); } */ // jakarta-oro try { r = compiler.compile(expr); } catch (MalformedPatternException ex) { System.err.println("MalformedPatternException: " + ex.getMessage()); System.err.println(expr); return(true); } // gnu-regexp /* try { r = new RE(expr); } catch (REException ex) { System.err.println("REException: " + ex.getMessage()); System.err.println(expr); return(true); } */ cacheInsertion.put(sparent, r); } // jakarta-regexp //boolean matched = r.match(cettexp); // jakarta-oro boolean matched = matcher.matches(cettexp, r); // gnu-regexp //boolean matched = r.isMatch(cettexp); return(matched); } /** * renvoit true si l'élément parent est valide par rapport à ses enfants (au niveau 1). * + renvoit l'expression régulière utilisée pour le test dans texpr[0] si details=true */ public boolean elementValide(JaxeElement parent, boolean details, String[] texpr) { if (schema == null) return(true); // on suppose que le test de sous-balise a déjà été fait if (autresConfigs.size() > 0) { Config conf = getElementConf((Element)parent.noeud); if (conf != this) return(true); // on ne peut pas tester, cf commentaire dans insertionPossible } Element sparent = schemaBaliseDef(localValue(parent.noeud.getNodeName())); Config conf = getElementConf((Element)parent.noeud); String namespace = parent.noeud.getNamespaceURI(); String cettexp = ""; NodeList lsousb = parent.noeud.getChildNodes(); for (int i=0; i 0 && sc.item(0) instanceof Element) return(true); } } } // simpleType Element st = getSchemaTypeElement(stype); if (st != null) return(true); } NodeList lsn = sbalisedef.getChildNodes(); for (int i=0; i 0); } else if (n instanceof Element && n.getLocalName().equals("simpleType")) return(true); } return(false); } else { NodeList ltexte = balisedef.getElementsByTagName("TEXTE"); if (ltexte.getLength() > 0) return(true); NodeList lsousb = balisedef.getElementsByTagName("SOUSBALISE"); for (int i=0; i" + sdoc.substring(ind + 1); ind = sdoc.indexOf('\n'); } sdoc = "" + sdoc + ""; } return(sdoc); } } return(null); } // Renvoit les fichiers XSL attachés à cette config et construit la table de hash des paramètres de ces fichiers // public File[] getXSLFiles() { public String[] getXSLFiles() { // actually, just the names, because they may be in JARs, and not loose Files if (jaxecfg == null) return(null); NodeList lxsl = jaxecfg.getElementsByTagName("FICHIERXSL"); // File[] fichiersxsl = new File[lxsl.getLength()] ; String[] fichiersxsl = new String[lxsl.getLength()] ; for (int i=0; i 0) valeur = (String)lval.get(0); else valeur = defaultvalue; return valeur; } protected Hashtable construireCacheParams(Element defbalise) { Hashtable hashparams = new Hashtable(); NodeList params = defbalise.getElementsByTagName("PARAMETRE"); for (int i=0; i 0) return(((Element)doctypes.item(0)).getAttribute("publicId")); else return(null); } public String getSystemId() { NodeList doctypes = jaxecfg.getElementsByTagName("DOCTYPE"); if (doctypes.getLength() > 0) return(((Element)doctypes.item(0)).getAttribute("systemId")); else return(null); } /** * Renvoit l'élément simpleType avec l'attribut name donné (sans le préfixe). * Renvoit null si aucun type simple n'est trouvé avec ce nom. */ public Element getSchemaTypeElement(String nomType) { for (int i=0; i