All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gate.gui.jape.JapeViewer Maven / Gradle / Ivy

Go to download

ANNIE is a general purpose information extraction system that provides the building blocks of many other GATE applications.

There is a newer version: 9.1
Show newest version
package gate.gui.jape;

import java.awt.BorderLayout;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;

import gate.LanguageAnalyser;
import gate.Resource;
import gate.creole.ANNIEConstants;
import gate.creole.AbstractProcessingResource;
import gate.creole.AbstractVisualResource;
import gate.creole.ResourceInstantiationException;
import gate.creole.ResourceReference;
import gate.creole.metadata.CreoleResource;
import gate.creole.metadata.GuiType;
import gate.event.ProgressListener;
import gate.jape.parser.ParseCpslConstants;
import gate.jape.parser.ParseCpslTokenManager;
import gate.jape.parser.SimpleCharStream;
import gate.jape.parser.Token;
import gate.util.BomStrippingInputStreamReader;
import gate.util.GateRuntimeException;

/**
 * A JAPE viewer that allows access to all phases of the grammar and
 * provides syntax highlighting. Future versions may allow editing and
 * reload of JAPE files.
 *
 * @author Mark A. Greenwood
 */
@CreoleResource(name="Jape Viewer", comment="A JAPE grammar file viewer", helpURL="http://gate.ac.uk/userguide/chap:jape", guiType=GuiType.LARGE, mainViewer=true, resourceDisplayed="gate.creole.Transducer")
public class JapeViewer extends AbstractVisualResource implements
                                                      ANNIEConstants,
                                                      ProgressListener {

  private static final long serialVersionUID = -6026605466406110590L;

  /**
   * The text area where the JAPE source will be displayed
   */
  private JTextPane textArea;

  /**
   * The tree in which the phases of the grammar will be shown
   */
  private JTree treePhases;

  private JScrollPane treeScroll;

  /**
   * A flag so we can know if we are currently reading a highlighting a
   * JAPE source file
   */
  private boolean updating = false;

  /**
   * The JAPE transducer for which we need to show the JAPE source
   */
  private LanguageAnalyser transducer;

  /**
   * A map that associates the syntactic elements of JAPE files with a
   * colour for performing syntax highlighting
   */
  private Map colorMap = new HashMap();

  /**
   * The default style used by the text area. This is used so that we
   * can ensure that normal text is displayed normally. This fixes a
   * problem where sometime the highlighting goes screwy and shows
   * everything as a comment.
   */
  private Style defaultStyle;

  @Override
  public Resource init() {
    initGuiComponents();
    return this;
  }

  private void initGuiComponents() {
    setLayout(new BorderLayout());
    textArea = new JTextPane();
    textArea.setEditable(false);
    JScrollPane textScroll = new JScrollPane(textArea,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    add(textScroll, BorderLayout.CENTER);

    treePhases = new JTree();
    treeScroll = new JScrollPane(treePhases,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    add(treeScroll, BorderLayout.WEST);
    treePhases.getSelectionModel().setSelectionMode(
            TreeSelectionModel.SINGLE_TREE_SELECTION);
    treePhases.addTreeSelectionListener(new TreeSelectionListener() {
      @Override
      public void valueChanged(TreeSelectionEvent e) {
        if(updating) return;
        if(e.getPath().getLastPathComponent() == null) return;

        try {
          readJAPEFileContents(new URL(((ResourceReference)transducer.getParameterValue("grammarURL")).toURL(), e.getPath()
                  .getLastPathComponent()
                  + ".jape"));
        }
        catch(IOException | ResourceInstantiationException ioe) {
          ioe.printStackTrace();
        }
      }
    });

    // if we want to set the jape to be monospaced (like most code
    // editors) then
    // do this...
    /*
     * MutableAttributeSet attrs = textArea.getInputAttributes();
     * StyleConstants.setFontFamily(attrs, "monospaced"); StyledDocument
     * doc = textArea.getStyledDocument(); doc.setCharacterAttributes(0,
     * doc.getLength() + 1, attrs, false);
     */
    defaultStyle = textArea.addStyle("default", null);

    Style style = textArea.addStyle("brackets", null);
    StyleConstants.setForeground(style, Color.red);
    colorMap.put(ParseCpslConstants.leftBrace, style);
    colorMap.put(ParseCpslConstants.rightBrace, style);
    colorMap.put(ParseCpslConstants.leftBracket, style);
    colorMap.put(ParseCpslConstants.rightBracket, style);
    colorMap.put(ParseCpslConstants.leftSquare, style);
    colorMap.put(ParseCpslConstants.rightSquare, style);

    style = textArea.addStyle("keywords", null);
    StyleConstants.setForeground(style, Color.blue);
    colorMap.put(ParseCpslConstants.rule, style);
    colorMap.put(ParseCpslConstants.priority, style);
    colorMap.put(ParseCpslConstants.macro, style);
    colorMap.put(ParseCpslConstants.bool, style);
    colorMap.put(ParseCpslConstants.phase, style);
    colorMap.put(ParseCpslConstants.input, style);
    colorMap.put(ParseCpslConstants.option, style);
    colorMap.put(ParseCpslConstants.multiphase, style);
    colorMap.put(ParseCpslConstants.phases, style);

    style = textArea.addStyle("strings", null);
    StyleConstants.setForeground(style, new Color(0, 128, 128));
    colorMap.put(ParseCpslConstants.string, style);

    style = textArea.addStyle("comments", null);
    StyleConstants.setForeground(style, new Color(0, 128, 0));
    colorMap.put(ParseCpslConstants.singleLineCStyleComment, style);
    colorMap.put(ParseCpslConstants.singleLineCpslStyleComment, style);
    colorMap.put(ParseCpslConstants.commentStart, style);
    colorMap.put(ParseCpslConstants.commentChars, style);
    colorMap.put(ParseCpslConstants.commentEnd, style);
    colorMap.put(ParseCpslConstants.phasesSingleLineCStyleComment, style);
    colorMap.put(ParseCpslConstants.phasesSingleLineCpslStyleComment, style);
    colorMap.put(ParseCpslConstants.phasesCommentStart, style);
    colorMap.put(ParseCpslConstants.phasesCommentChars, style);
    colorMap.put(ParseCpslConstants.phasesCommentEnd, style);
  }

  @Override
  public void setTarget(final Object target) {
    if(target == null) {
     throw new NullPointerException("JAPE viewer received a null target");
    }
    // check that the target is one we can work with - it needs to be a
    // LanguageAnalyser that has grammarURL and encoding parameters.
    boolean targetOK = true;
    if(target instanceof LanguageAnalyser) {
      try {
        Object grammarURL = ((LanguageAnalyser)target).getParameterValue("grammarURL");
        if (grammarURL == null) {
          throw new IllegalArgumentException("The GATE JAPE viewer can't be used with serialised grammars");
        }
        
        ((LanguageAnalyser)target).getParameterValue("encoding");
      } catch(ResourceInstantiationException rie) {
        targetOK = false;
      }
    } else {
      targetOK = false;
    }
     
    if(!targetOK) {
      throw new IllegalArgumentException(
              "The GATE JAPE viewer can only be used with a GATE jape transducer!\n"
                      + target.getClass().toString()
                      + " is not a GATE JAPE Transducer!");
    }
    
    SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {

        if(transducer != null && transducer instanceof AbstractProcessingResource) {
          ((AbstractProcessingResource)transducer).removeProgressListener(JapeViewer.this);
        }
    
        transducer = (LanguageAnalyser)target;
        URL japeFileURL = null;
        
        try {
          japeFileURL = ((ResourceReference)transducer.getParameterValue("grammarURL")).toURL();
        }
        catch (ResourceInstantiationException | IOException rie) {
          //ignore this for now and let the null catch take over
          rie.printStackTrace();
        }


        if(japeFileURL == null) {
          textArea.setText("The source for this JAPE grammar is not available!");
          remove(treeScroll);
          return;
        }

        String japePhaseName = japeFileURL.getFile();
        japePhaseName = japePhaseName.substring(japePhaseName.lastIndexOf("/") + 1,
                japePhaseName.length() - 5);
        treePhases.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(
                japePhaseName)));
        treePhases.setSelectionRow(0);

        readJAPEFileContents(japeFileURL);
        if(transducer instanceof AbstractProcessingResource) {
          ((AbstractProcessingResource)transducer).addProgressListener(JapeViewer.this);
        }
      }
      
    });
    
  }

  private void readJAPEFileContents(URL url) {
    if(treePhases.getLastSelectedPathComponent() == null) return;
    updating = true;

    try {
      Reader japeReader = null;
      String encoding = (String)transducer.getParameterValue("encoding");
      if(encoding == null) {
        japeReader = new BomStrippingInputStreamReader(url.openStream());
      }
      else {
        japeReader = new BomStrippingInputStreamReader(url.openStream(), encoding);
      }
      BufferedReader br = new BufferedReader(japeReader);
      String content = br.readLine();
      StringBuilder japeFileContents = new StringBuilder();
      List lineOffsets = new ArrayList();

      while(content != null) {
        lineOffsets.add(japeFileContents.length());

        // replace tabs with spaces otherwise the highlighting fails
        // TODO work out why this is needed and fix it properly
        japeFileContents.append(content.replaceAll("\t", "   ")).append("\n");
        content = br.readLine();
      }

      textArea.setText(japeFileContents.toString());
      textArea.updateUI();
      br.close();

      ParseCpslTokenManager tokenManager = new ParseCpslTokenManager(
              new SimpleCharStream(
                      new StringReader(japeFileContents.toString())));

      StyledDocument doc = textArea.getStyledDocument();

      doc.setCharacterAttributes(0, japeFileContents.length(), defaultStyle,
              true);

      ((DefaultMutableTreeNode)treePhases.getSelectionPath()
              .getLastPathComponent()).removeAllChildren();

      Token t;
      while((t = tokenManager.getNextToken()).kind != 0) {

        Token special = t.specialToken;
        while(special != null) {
          Style style = colorMap.get(special.kind);
          if(style != null) {
            int start = lineOffsets.get(special.beginLine - 1)
                    + special.beginColumn - 1;
            int end = lineOffsets.get(special.endLine - 1) + special.endColumn
                    - 1;
            doc.setCharacterAttributes(start, end - start + 1, style, true);
          }

          special = special.specialToken;
        }

        Style style = colorMap.get(t.kind);

        if(style != null) {
          int start = lineOffsets.get(t.beginLine - 1) + t.beginColumn - 1;
          int end = lineOffsets.get(t.endLine - 1) + t.endColumn - 1;
          doc.setCharacterAttributes(start, end - start + 1, style, true);
        }

        if(t.kind == ParseCpslConstants.path) {
          ((DefaultMutableTreeNode)treePhases.getSelectionPath()
                  .getLastPathComponent()).add(new DefaultMutableTreeNode(t
                  .toString()));
        }
      }
    }
    catch(IOException ioe) {
      throw new GateRuntimeException(ioe);
    } catch(ResourceInstantiationException rie) {
      throw new GateRuntimeException(rie);
    }

    if(treePhases.getSelectionRows() != null
            && treePhases.getSelectionRows().length > 0)
      treePhases.expandRow(treePhases.getSelectionRows()[0]);

    updating = false;
  }

  @Override
  public void processFinished() {
    setTarget(transducer);
  }

  @Override
  public void progressChanged(int progress) {

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy