Package tk.eclipse.plugin.xmleditor.editors

Source Code of tk.eclipse.plugin.xmleditor.editors.XMLEditor

package tk.eclipse.plugin.xmleditor.editors;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import jp.aonir.fuzzyxml.FuzzyXMLDocType;
import jp.aonir.fuzzyxml.FuzzyXMLDocument;
import jp.aonir.fuzzyxml.FuzzyXMLElement;
import jp.aonir.fuzzyxml.FuzzyXMLNode;
import jp.aonir.fuzzyxml.FuzzyXMLParser;
import jp.aonir.fuzzyxml.internal.RenderContext;
import jp.aonir.fuzzyxml.internal.WOHTMLRenderDelegate;

import org.apache.xerces.parsers.SAXParser;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.objectstyle.wolips.wodclipse.core.Activator;
import org.objectstyle.wolips.wodclipse.core.preferences.PreferenceConstants;
import org.xml.sax.InputSource;

import tk.eclipse.plugin.htmleditor.HTMLPlugin;
import tk.eclipse.plugin.htmleditor.HTMLProjectParams;
import tk.eclipse.plugin.htmleditor.HTMLUtil;
import tk.eclipse.plugin.htmleditor.editors.HTMLConfiguration;
import tk.eclipse.plugin.htmleditor.editors.HTMLSourceEditor;
import tk.eclipse.plugin.htmleditor.editors.IHTMLOutlinePage;

/**
* The XML editor.
*
* @author Naoki Takezoe
*/
public class XMLEditor extends HTMLSourceEditor {
 
  private ArrayList<IDTDResolver> resolvers = new ArrayList<IDTDResolver>();
 
  public static final String GROUP_XML = "_xml";
  public static final String ACTION_GEN_DTD = "_generate_dtd";
  public static final String ACTION_GEN_XSD = "_generate_xsd";
  public static final String ACTION_ESCAPE_XML = "_escape_xml";
  public static final String ACTION_FORMAT_XML = "_format_xml";
 
  private String[] _classNameAttributes = null;
  private List<ElementSchemaMapping> _schemaMappings = null;
 
  /**
   * The constructor.
   */
  public XMLEditor() {
    this(new XMLConfiguration(HTMLPlugin.getDefault().getColorProvider()));
  }
 
  /**
   * The constructor for customize this editor.
   * <p>
   * This editor is initialized with the given <code>XMLConfiguration</code>.
   *
   * @param config the editor configuration for this editor
   */
  public XMLEditor(XMLConfiguration config){
    super(config);
   
    setAction(ACTION_GEN_DTD,new GenerateDTDAction());
    setAction(ACTION_GEN_XSD,new GenerateXSDAction());
    setAction(ACTION_ESCAPE_XML, new EscapeXMLAction());
  }
 
  @Override
  protected void createActions() {
      super.createActions();
      // add content content format action
    setAction(ACTION_FORMAT_XML, new FormatXMLAction());
  }
 
  /** This method is called when configuration is changed. */
  @Override
  protected void handlePreferenceStoreChanged(PropertyChangeEvent event){
    super.handlePreferenceStoreChanged(event);
    _classNameAttributes = null;
    _schemaMappings = null;
  }
 
  public List<ElementSchemaMapping> getSchemaMappings(){
    if(_schemaMappings==null){
      _schemaMappings = ElementSchemaMapping.loadFromPreference();
    }
    return _schemaMappings;
  }
 
  public String[] getClassNameAttributes(){
    if(_classNameAttributes==null){
      // Load classname attrs from the preference store
      IPreferenceStore store = getPreferenceStore();
      if(store.getBoolean(HTMLPlugin.PREF_ENABLE_CLASSNAME)){
        _classNameAttributes = StringConverter.asArray(
            store.getString(HTMLPlugin.PREF_CLASSNAME_ATTRS));
      } else {
        _classNameAttributes = new String[0];
      }
    }
    return _classNameAttributes;
  }
 
  /**
   * Returns the <code>XMLOutlinePage</code>.
   *
   * @see XMLOutlinePage
   */
  @Override
  protected IHTMLOutlinePage createOutlinePage(){
    return new XMLOutlinePage(this);
  }

  /**
   * Adds <code>IDTDResolver</code>.
   *
   * @param resolver IDTDResolver
   */
  public void addDTDResolver(IDTDResolver resolver){
    resolvers.add(resolver);
  }
 
  /**
   * Returns an array of <code>IDTDResolver</code>
   * that was added by <code>addEntityResolver()</code>.
   *
   * @return an array of <code>IDTDResolver</code>
   */
  public IDTDResolver[] getDTDResolvers(){
    return resolvers.toArray(new IDTDResolver[resolvers.size()]);
  }
 
  /**
   * Validates the XML document.
   * <p>
   * If <code>getValidation()</code> returns <code>false</code>,
   * this method do nothing.
   */
  @Override
  protected void doValidate(){
    try {
      ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
        public void run(IProgressMonitor monitor) throws CoreException {
          try {
            IFileEditorInput input = (IFileEditorInput)getEditorInput();
            String xml = getDocumentProvider().getDocument(input).get();
            IFile resource = input.getFile();
            //String charset = resource.getCharset();
            //charset = "Shift_JIS";
            resource.deleteMarkers(IMarker.PROBLEM,false,0);
           
            HTMLProjectParams params = new HTMLProjectParams(resource.getProject());
            if(!params.getValidateXML()){
              return;
            }
           
            if(params.getUseDTD()==false){
              // remove DOCTYPE decl
              Matcher matcher = patternDoctypePublic.matcher(xml);
              if(matcher.find()){
                xml = removeMatched(xml,matcher.start(),matcher.end());
              }
              matcher = patternDoctypeSystem.matcher(xml);
              if(matcher.find()){
                xml = removeMatched(xml,matcher.start(),matcher.end());
              }
            }
           
            SAXParser parser = new SAXParser();
           
            String   dtd = getDTD(xml, false);
            String[] xsd = getXSD(xml, false);
           
            // Validation configuration
            if((dtd==null && xsd==null) || !params.getUseDTD()){
              parser.setFeature("http://xml.org/sax/features/validation", false);
            } else {
              parser.setFeature("http://xml.org/sax/features/validation", true);
              parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
            }
            if(xsd!=null && params.getUseDTD()){
//              parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
//              parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", xsd);
              parser.setFeature("http://apache.org/xml/features/validation/schema", true);
              parser.setFeature("http://xml.org/sax/features/namespaces", true);
            }
           
            parser.setFeature("http://xml.org/sax/features/use-entity-resolver2", true);
            parser.setEntityResolver(new DTDResolver(getDTDResolvers(),
                input.getFile().getLocation().makeAbsolute().toFile().getParentFile()));
            parser.setErrorHandler(new XMLValidationHandler(resource));
           
            parser.parse(new InputSource(new StringReader(xml))); //new ByteArrayInputStream(xml.getBytes(charset))));
           
          } catch(Exception ex){
            // ignore
            //HTMLPlugin.logException(ex);
          }
        }
      },null);
    } catch(Exception ex){
      HTMLPlugin.logException(ex);
    }
  }
 
  /** replace to whitespaces */
  private String removeMatched(String source,int start,int end){
    StringBuffer sb = new StringBuffer();
    sb.append(source.substring(0,start));
    for(int i=start;i<end + 1;i++){
      char c = source.charAt(i);
      if(c=='\r' || c=='\n'){
        sb.append(c);
      } else {
        sb.append(" ");
      }
    }
    sb.append(source.substring(end+1,source.length()));
    return sb.toString();
  }
 
  /**
   * Returns URI of DTD (SystemID) which is used in the document.
   * If any DTD isn't used, this method returns <code>null</code>.
   *
   * @param xml XML
   * @return URL of DTD
   */
  public String getDTD(String xml, boolean useElementMapping){
    // PUBLIC Identifier
    Matcher matcher = patternDoctypePublic.matcher(xml);
    if(matcher.find()){
      return matcher.group(2);
    }
    // SYSTEM Identifier
    matcher = patternDoctypeSystem.matcher(xml);
    if(matcher.find()){
      return matcher.group(1);
    }
   
    // Root element mappings
    if(useElementMapping){
      String firstTag = getFirstTag(xml);
      if(firstTag!=null){
        List<ElementSchemaMapping> schemaMappings = getSchemaMappings();
        for(int i=0;i<schemaMappings.size();i++){
          ElementSchemaMapping mapping = schemaMappings.get(i);
          if(mapping.getRootElement().equals(firstTag) && mapping.getFilePath().endsWith(".dtd")){
            return "file:" + mapping.getFilePath();
          }
        }
      }
    }
   
    return null;
  }
 
  /**
   * Returns URI (schema location) of XML schema which is used in the document.
   * If any XML schema isn't used, this method returns <code>null</code>.
   *
   * @param xml XML
   * @return URL of XML schema
   */
  public String[] getXSD(String xml, boolean useElementMapping){
    // PUBLIC Identifier
    Matcher matcher = patternNsXSD.matcher(xml);
    if(matcher.find()){
      String matched = matcher.group(1).trim();
      matched.replaceAll("\r\n","\n");
      matched.replaceAll("\r","\n");
      String[] xsd = matched.split("\n| |\t");
      for(int i=0;i<xsd.length;i++){
        xsd[i] = xsd[i].trim();
      }
      return xsd;
    }
    matcher = patternNoNsXSD.matcher(xml);
    if(matcher.find()){
      return new String[]{matcher.group(3).trim()};
    }
   
    // Root element mappings
    if(useElementMapping){
      String firstTag = getFirstTag(xml);
      if(firstTag!=null){
        List schemaMappings = getSchemaMappings();
        for(int i=0;i<schemaMappings.size();i++){
          ElementSchemaMapping mapping = (ElementSchemaMapping)schemaMappings.get(i);
          if(mapping.getRootElement().equals(firstTag) && mapping.getFilePath().endsWith(".xsd")){
            return new String[]{ "file:" + mapping.getFilePath() };
          }
        }
      }
    }
   
    return null;
  }
 
  /**
   * Extracts the first element name in the given xml source.
   */
  private static String getFirstTag(String xml){
    FuzzyXMLDocument doc = new FuzzyXMLParser(false).parse(xml);
    FuzzyXMLNode[] nodes = doc.getDocumentElement().getChildren();
    for(int i=0;i<nodes.length;i++){
      if(nodes[i] instanceof FuzzyXMLElement){
        return ((FuzzyXMLElement)nodes[i]).getName();
      }
    }
    return null;
  }
 
  /** Reular expressions to get DOCTYPE declaration */
  private Pattern patternDoctypePublic
    = Pattern.compile("<!DOCTYPE[\\s\r\n]+?[^<]+?[\\s\r\n]+?PUBLIC[\\s\r\n]*?\"(.+?)\"[\\s\r\n]*?\"(.+?)\".*?>",Pattern.DOTALL);
  private Pattern patternDoctypeSystem
    = Pattern.compile("<!DOCTYPE[\\s\r\n]+?[^<]+?[\\s\r\n]+?SYSTEM[\\s\r\n]*?\"(.+?)\".*?>",Pattern.DOTALL);
 
  /** Reular expressions to get schema location of XMLschema */
  private Pattern patternNsXSD
    = Pattern.compile("schemaLocation[\\s\r\n]*?=[\\s\r\n]*?\"(.+?)\"",Pattern.DOTALL);
  private Pattern patternNoNsXSD
    = Pattern.compile("noNamespaceSchemaLocation[\\s\r\n]*?=[\\s\r\n]*?\"(.+?)\"",Pattern.DOTALL);
 
  /**
   * Update informations about code-completion.
   */
  @Override
  protected void updateAssist(){
    try {
      XMLConfiguration config = (XMLConfiguration)getSourceViewerConfiguration();
      config.getClassNameHyperlinkProvider().setEditor(this);
     
      if(!isFileEditorInput()){
        return;
      }
      super.updateAssist();
     
      IFileEditorInput input = (IFileEditorInput)getEditorInput();
      HTMLProjectParams params = new HTMLProjectParams(input.getFile().getProject());
      if(params.getUseDTD()==false){
        return;
      }
     
      String xml = getDocumentProvider().getDocument(input).get();
     
      // Update DTD based completion information.
      String dtd = getDTD(xml, true);
      if(dtd!=null){
        DTDResolver resolver = new DTDResolver(getDTDResolvers(),
            input.getFile().getLocation().makeAbsolute().toFile().getParentFile());
        InputStream in = resolver.getInputStream(dtd);
        if(in!=null){
          Reader reader = new InputStreamReader(in);
          // update AssistProcessor
          XMLAssistProcessor assistProcessor =
            (XMLAssistProcessor)((HTMLConfiguration)getSourceViewerConfiguration()).getAssistProcessor();
          assistProcessor.updateDTDInfo(reader);
          reader.close();
        }
      }
     
      // Update XML Schema based completion information.
      String[] xsd = getXSD(xml, true);
      if(xsd!=null){
        DTDResolver resolver = new DTDResolver(getDTDResolvers(),
            input.getFile().getLocation().makeAbsolute().toFile().getParentFile());
        for(int i=0;i<xsd.length;i++){
          InputStream in = resolver.getInputStream(xsd[i]);
          if(in!=null){
            Reader reader = new InputStreamReader(in);
            // update AssistProcessor
            XMLAssistProcessor assistProcessor =
              (XMLAssistProcessor)((HTMLConfiguration)getSourceViewerConfiguration()).getAssistProcessor();
            assistProcessor.updateXSDInfo(xsd[i],reader);
            reader.close();
          }
        }
      }
    } catch(Exception ex){
      HTMLPlugin.logException(ex);
    }
  }
 
  @Override
  protected void addContextMenuActions(IMenuManager menu){
    menu.add(new Separator(GROUP_HTML));
    //addAction(menu,GROUP_HTML,ACTION_OPEN_PALETTE);
    addAction(menu,GROUP_HTML,ACTION_ESCAPE_XML);
    addAction(menu,GROUP_HTML,ACTION_COMMENT);
    addAction(menu,GROUP_HTML,ACTION_FORMAT_XML);
    menu.add(new Separator(GROUP_XML));
    addAction(menu,GROUP_XML,ACTION_GEN_DTD);
    addAction(menu,GROUP_XML,ACTION_GEN_XSD);
  }
 
  @Override
  protected void updateSelectionDependentActions() {
    super.updateSelectionDependentActions();
    ITextSelection sel = (ITextSelection)getSelectionProvider().getSelection();
    if(sel.getText().equals("")){
      getAction(ACTION_ESCAPE_XML).setEnabled(false);
    } else {
      getAction(ACTION_ESCAPE_XML).setEnabled(true);
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////
  // actions
  //////////////////////////////////////////////////////////////////////////// 
  /**
   * The action to escape XML special chars in the selection.
   */
  private class EscapeXMLAction extends Action {
   
    public EscapeXMLAction(){
      super(HTMLPlugin.getResourceString("HTMLEditor.EscapeAction"));
      setEnabled(false);
      setAccelerator(SWT.CTRL | '\\');
    }
   
    @Override
    public void run() {
      ITextSelection sel = (ITextSelection)getSelectionProvider().getSelection();
      IDocument doc = getDocumentProvider().getDocument(getEditorInput());
      try {
        doc.replace(sel.getOffset(),sel.getLength(),HTMLUtil.escapeXML(sel.getText()));
      } catch (BadLocationException e) {
        HTMLPlugin.logException(e);
      }
    }
  }
 
  /**
   * The action to generate DTD from XML.
   */
  private class GenerateDTDAction extends Action {
    public GenerateDTDAction(){
      super(HTMLPlugin.getResourceString("XMLEditor.GenerateDTD"),
          HTMLPlugin.getDefault().getImageRegistry().getDescriptor(HTMLPlugin.ICON_DTD));
    }
    @Override
    public void run() {
      FileDialog dialog = new FileDialog(getViewer().getTextWidget().getShell(),SWT.SAVE);
      dialog.setFilterExtensions(new String[]{"*.dtd"});
      String file = dialog.open();
      if(file!=null){
        try {
          SchemaGenerator.generateDTDFromXML(getFile(), new File(file));
        } catch(Exception ex){
          HTMLPlugin.openAlertDialog(ex.toString());
        }
      }
    }
  }
 
  /**
   * The action to generate XML schema from XML.
   */
  private class GenerateXSDAction extends Action {
    public GenerateXSDAction(){
      super(HTMLPlugin.getResourceString("XMLEditor.GenerateXSD"),
          HTMLPlugin.getDefault().getImageRegistry().getDescriptor(HTMLPlugin.ICON_XSD));
    }
    @Override
    public void run() {
      FileDialog dialog = new FileDialog(getViewer().getTextWidget().getShell(),SWT.SAVE);
      dialog.setFilterExtensions(new String[]{"*.xsd"});
      String file = dialog.open();
      if(file!=null){
        try {
          SchemaGenerator.generateXSDFromXML(getFile(), new File(file));
        } catch(Exception ex){
          HTMLPlugin.openAlertDialog(ex.toString());
        }
      }
    }
  }
 
  /**
   * The action to format (correct indentation) XML.
   */
  private class FormatXMLAction extends Action {
    public FormatXMLAction(){
      super(HTMLPlugin.getResourceString("XMLEditor.FormatXML"));
      setActionDefinitionId("tk.eclipse.plugin.xmleditor.format");
    }
    @Override
    public void run(){
      try {
        IDocument xmlDocument = getDocumentProvider().getDocument(getEditorInput());
        String xmlString = xmlDocument.get();
        FuzzyXMLDocument htmlModel = new FuzzyXMLParser(false, false).parse(xmlString);
        FuzzyXMLElement documentElement = htmlModel.getDocumentElement();

        IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
        RenderContext renderContext = new RenderContext(true);
        renderContext.setShowNewlines(true);
        renderContext.setIndentSize(prefs.getInt(PreferenceConstants.INDENT_SIZE));
        renderContext.setIndentTabs(prefs.getBoolean(PreferenceConstants.INDENT_TABS));
        renderContext.setTrim(true);
        renderContext.setLowercaseAttributes(prefs.getBoolean(PreferenceConstants.LOWERCASE_ATTRIBUTES));
        renderContext.setLowercaseTags(prefs.getBoolean(PreferenceConstants.LOWERCASE_TAGS));
        renderContext.setSpacesAroundEquals(prefs.getBoolean(PreferenceConstants.SPACES_AROUND_EQUALS));
        renderContext.setSpaceInEmptyTags(true);
        renderContext.setAddMissingQuotes(true);
        renderContext.setDelegate(new WOHTMLRenderDelegate(prefs.getBoolean(PreferenceConstants.STICKY_WOTAGS)));

        StringBuffer xmlBuffer = new StringBuffer();
        FuzzyXMLDocType docType = htmlModel.getDocumentType();
        if (docType != null) {
          docType.toXMLString(renderContext, xmlBuffer);
        }
        for (FuzzyXMLNode node : documentElement.getChildren()) {
          node.toXMLString(renderContext, xmlBuffer);
          //htmlBuffer.append("\n");
        }
        xmlDocument.set(xmlBuffer.toString().trim());

//        // Format XML using XSL
//        IEditorInput input = getEditorInput();
//        String charset = System.getProperty("file.encoding");
//        if(input instanceof IFileEditorInput){
//          charset = ((IFileEditorInput)input).getFile().getCharset();
//        }
//       
//        TransformerFactory transFactory = TransformerFactory.newInstance();
//       
//        String xsl = new String(HTMLUtil.readStream(
//            XMLEditor.class.getResourceAsStream("format.xsl")));
//        xsl = xsl.replaceAll("\\$\\{charset\\}", charset);
//       
//        Source xslSource = new StreamSource(new StringReader(xsl));
//        Transformer transformer = transFactory.newTransformer(xslSource);
//       
//        IDocument doc = getDocumentProvider().getDocument(getEditorInput());
//        ByteArrayOutputStream out = new ByteArrayOutputStream();
//       
//       
//        Source source = new StreamSource(new StringReader(doc.get()));
//        Result result = new StreamResult(out);
//       
//        transformer.transform(source, result);
       
        //doc.set(new String(out.toByteArray(), charset));
       
      } catch(Exception ex){
        HTMLPlugin.openAlertDialog(ex.toString());
      }
    }
  }
}
TOP

Related Classes of tk.eclipse.plugin.xmleditor.editors.XMLEditor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.