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

jlibs.nblr.editor.NBLREditor Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/**
 * Copyright 2015 Santhosh Kumar Tekuri
 *
 * The JLibs authors license this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package jlibs.nblr.editor;

import jlibs.core.lang.JavaUtil;
import jlibs.core.lang.OS;
import jlibs.nblr.Syntax;
import jlibs.nblr.editor.debug.Debugger;
import jlibs.nblr.matchers.Matcher;
import jlibs.nblr.rules.Edge;
import jlibs.nblr.rules.Node;
import jlibs.nblr.rules.Routes;
import jlibs.nblr.rules.Rule;
import org.netbeans.api.visual.action.EditProvider;
import org.netbeans.api.visual.action.TwoStateHoverProvider;
import org.netbeans.api.visual.widget.Widget;

import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

import static jlibs.nblr.matchers.Matcher.*;

/**
 * @author Santhosh Kumar T
 */
public class NBLREditor extends JFrame{
    private Syntax syntax;
    private RuleScene scene;
    private JComboBox combo;
    private JLabel message = new JLabel();
    Debugger debugger;

    public NBLREditor(Syntax syntax){
        super("Untitled");
        
        JMenuBar menubar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");
        int ctrl = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
        fileMenu.add(newAction).setAccelerator(KeyStroke.getKeyStroke('N', ctrl));
        fileMenu.add(openAction).setAccelerator(KeyStroke.getKeyStroke('O', ctrl));
        fileMenu.add(saveAction).setAccelerator(KeyStroke.getKeyStroke('S', ctrl));
        fileMenu.add(saveAsAction).setAccelerator(KeyStroke.getKeyStroke('S', ctrl|InputEvent.SHIFT_DOWN_MASK));
        menubar.add(fileMenu);
        setJMenuBar(menubar);

        combo = createRulesCombo();
        if(OS.get()!=OS.MAC)
            combo.setPreferredSize(new Dimension(10, 5));
        combo.setFont(Util.FIXED_WIDTH_FONT);
        scene = new RuleScene(new TwoStateHoverProvider() {
            @Override
            public void unsetHovering(Widget widget){
                String msg = "";
                Rule rule = scene.getRule();
                if(rule!=null){
                    List usages = scene.getSyntax().usages(rule);
                    if(usages.size()==0)
                        msg = "this is primary rule";
                    else if(usages.size()==1){
                        if(usages.get(0)!=rule)
                            msg = "this rule can be inlined in "+usages.get(0).name;
                    }

//                    for(Rule r: scene.getSyntax().rules.values())
//                        r.computeIDS();
//                    System.out.println("=============================================");
//                    SyntaxClass syntaxClass = new SyntaxClass(scene.getSyntax());
//                    System.out.println(new RuleMethod(syntaxClass, rule));
                }
                message.setText(msg);
            }

            @Override
            public void setHovering(Widget widget){
                String msg = "";
                Object model = Util.model(widget);
                if(model instanceof Node){
                    Node node = (Node)model;
                    try{
                        msg = new Routes(scene.getRule(), node, true).toString();
                    }catch(IllegalStateException ex){
                        msg = ex.getMessage();
                    }
                }else if(model instanceof Edge){
                    Edge edge = (Edge)model;
                    if(edge.matcher!=null)
                        msg = edge.matcher.toString();
                }
                message.setText(msg);
            }
        }, new EditProvider(){
            @Override
            public void edit(Widget widget){
                Edge edge = Util.edge(widget);
                if(edge.ruleTarget!=null){
                    history.push(new SceneState(scene));
                    upAction.setEnabled(true);
                    userRuleSelection = false;
                    combo.setSelectedItem(edge.ruleTarget.rule);
                    userRuleSelection = true;
                }
            }
        }){
            @Override
            public void setRule(Syntax syntax, Rule rule){
                super.setRule(syntax, rule);
                if(rule!=null && rule.id0)
            rule = syntax.rules.values().iterator().next();
        scene.setRule(syntax, rule);
    }

    /*-------------------------------------------------[ Actions ]---------------------------------------------------*/

    private ImageIcon icon(String name){
        return new ImageIcon(getClass().getResource(name));
    }
    
    @SuppressWarnings({"FieldCanBeLocal"})
    private Action newAction = new AbstractAction("New"){
        public void actionPerformed(ActionEvent ae){
            scene.file = null;
            Syntax syntax = new Syntax();
            syntax.add("ANY", any());
            Rule rule = new Rule();
            rule.node = new Node();
            syntax.add("RULE1", rule);
            showSyntax(syntax);
            getRootPane().putClientProperty("Window.documentFile", null);
            setTitle("Untitled");
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action openAction = new AbstractAction("Open..."){
        public void actionPerformed(ActionEvent ae){
            JFileChooser chooser = new JFileChooser();
            if(scene.file!=null)
                chooser.setCurrentDirectory(scene.file.getParentFile());
            if(chooser.showOpenDialog(NBLREditor.this)==JFileChooser.APPROVE_OPTION){
                File selected = chooser.getSelectedFile();
                try{
                    showSyntax(Syntax.loadFrom(selected));
                    scene.file = selected;
                    getRootPane().putClientProperty("Window.documentFile", scene.file);
                    setTitle(scene.file.getName());
                }catch(Exception ex){
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(NBLREditor.this, ex.getMessage(), ex.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    };

    private void saveTo(File file){
        if(file==null){
            JFileChooser chooser = new JFileChooser();
            if(scene.file!=null)
                chooser.setCurrentDirectory(scene.file.getParentFile());
            if(chooser.showSaveDialog(NBLREditor.this)==JFileChooser.APPROVE_OPTION)
                file = chooser.getSelectedFile();
            else
                return;
        }
        try{
            syntax.saveTo(file);
            scene.file = file;
        }catch(Exception ex){
            ex.printStackTrace();
            JOptionPane.showMessageDialog(NBLREditor.this, ex.getMessage(), ex.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);
        }
    }

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action saveAction = new AbstractAction("Save"){
        public void actionPerformed(ActionEvent ae){
            saveTo(scene.file);
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action saveAsAction = new AbstractAction("Save As..."){
        public void actionPerformed(ActionEvent ae){
            saveTo(null);
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action newRuleAction = new AbstractAction("New Rule...", icon("newRule.png")){
        public void actionPerformed(ActionEvent ae){
            String ruleName = JOptionPane.showInputDialog("Rule Name");
            if(ruleName!=null){
                if(syntax.rules.get(ruleName)!=null){
                    JOptionPane.showMessageDialog(NBLREditor.this, "Rule with name '"+ruleName+"' already exists");
                    return;
                }
                if(JavaUtil.KEYWORDS.contains(ruleName)){
                    JOptionPane.showMessageDialog(NBLREditor.this, "Rule name cannot be java keyword");
                    return;
                }

                Rule rule = new Rule();
                rule.node = new Node();
                syntax.add(ruleName, rule);
                combo.setModel(new DefaultComboBoxModel(syntax.rules.values().toArray()));
                if(combo.getItemCount()==1)
                    scene.setRule(syntax, rule);
                else
                    combo.setSelectedIndex(combo.getItemCount()-1);
            }
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action renameRuleAction = new AbstractAction("Rename Rule...", icon("renameRule.png")){
        public void actionPerformed(ActionEvent ae){
            Rule rule = (Rule)combo.getSelectedItem();
            if(rule==null)
                JOptionPane.showMessageDialog(NBLREditor.this, "No Rule to rename");
            else{
                String newName = JOptionPane.showInputDialog("Rule Name", rule.name);
                if(newName!=null){
                    if(newName.equals(rule.name))
                        return;

                    if(syntax.rules.get(newName)!=null){
                        JOptionPane.showMessageDialog(NBLREditor.this, "Rule with name '"+newName+"' already exists");
                        return;
                    }
                    if(JavaUtil.KEYWORDS.contains(newName)){
                        JOptionPane.showMessageDialog(NBLREditor.this, "Rule name cannot be java keyword");
                        return;
                    }

                    rule.name = newName;
                    Rule rules[] = syntax.rules.values().toArray(new Rule[syntax.rules.size()]);
                    syntax.rules.clear();
                    for(Rule r: rules)
                        syntax.add(r.name, r);
                    combo.repaint();
                }
            }
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action deleteRuleAction = new AbstractAction("Delete Rule...", icon("deleteRule.png")){
        public void actionPerformed(ActionEvent ae){
            Rule rule = (Rule)combo.getSelectedItem();
            if(rule==null)
                JOptionPane.showMessageDialog(NBLREditor.this, "No Rule to delete");
            else{
                for(Rule r: syntax.rules.values()){
                    if(r!=rule){
                        for(Edge edge: r.edges()){
                            if(edge.ruleTarget!=null && edge.ruleTarget.rule==rule){
                                JOptionPane.showMessageDialog(NBLREditor.this, "This rule is used by Rule '"+r.name+",");
                                return;
                            }
                        }
                    }
                }
                if(JOptionPane.showConfirmDialog(NBLREditor.this, "Are you sure, you want to delete this rule?", "Confirm", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){
                    syntax.delete(rule);
                    combo.setModel(new DefaultComboBoxModel(syntax.rules.values().toArray()));
                    scene.setRule(syntax, (Rule)combo.getSelectedItem());
                }
            }
        }
    };

    @SuppressWarnings({"FieldCanBeLocal"})
    private Action upAction = new AbstractAction("Up", icon("up.png")){
        public void actionPerformed(ActionEvent ae){
            SceneState sceneState = history.pop();
            userRuleSelection = false;
            combo.setSelectedItem(sceneState.rule);
            ((JViewport)scene.getView().getParent()).setViewPosition(sceneState.position);
            userRuleSelection = true;
            setEnabled(!history.isEmpty());
        }
    };

    private final Deque history = new ArrayDeque();
    private boolean userRuleSelection = true;
    static class SceneState{
        Rule rule;
        Point position;

        SceneState(RuleScene scene){
            rule = scene.getRule();
            position = ((JViewport)scene.getView().getParent()).getViewPosition();
        }
    }

    /*-------------------------------------------------[ Test ]---------------------------------------------------*/

    @SuppressWarnings({"UnusedDeclaration"})
    public static void main(String[] args){
        Syntax syntax = new Syntax();

        Matcher Q               = syntax.add("Q",              ch('\''));
        Matcher DIGIT           = syntax.add("DIGIT",          range("0-9"));
        Matcher PUBID_CHAR     = syntax.add("PUBID_CHAR",   or(any(" \r\n"), range("A-Z"), range("a-z"), DIGIT, any("-'()+,./:=?;!*#@$_%")));
        Matcher PUBID_CHAR_NQ     = syntax.add("PUBID_CHAR_NQ",   minus(PUBID_CHAR, Q));
        Matcher SPECIAL_CHAR     = syntax.add("SPECIAL_CHAR",   any("^[]-^&\\<"));
        Matcher NON_SPECIAL_CHAR = syntax.add("NON_SPECIAL_CHAR",   not(SPECIAL_CHAR));


//        Matcher GT              = syntax.add("GT",             ch('>'));
//        Matcher BRACKET_CLOSE   = syntax.add("BRACKET_CLOSE",  ch(']'));
//        Matcher DQ              = syntax.add("DQ",             ch('"'));

//        Matcher HEX_DIGIT       = syntax.add("HEX_DIGIT",      or(DIGIT, range("a-f"), range("A-F")));
//        Matcher WS              = syntax.add("WS",             any(" \t\n\r"));
//        Matcher ENCODING_START  = syntax.add("ENCODING_START", or(range("A-Z"), range("a-z")));
//        Matcher ENCODING_PART   = syntax.add("ENCODING_PART",  or(ENCODING_START, DIGIT, any("._-")));
//        Matcher CHAR            = syntax.add("CHAR",           or(any("\t\n\r"), range(" -\uD7FF"), range("\uE000-\uFFFD")/*, range("\u10000-\u10FFFF")*/));
//        Matcher DASH            = syntax.add("DASH",           ch('-'));
//        Matcher NDASH           = syntax.add("NDASH",          minus(CHAR, ch('-')));
//        Matcher NAME_START      = syntax.add("NAME_START",     or(ch(':'), range("A-Z"), ch('_'), range("a-z"), range("\u00C0-\u00D6"), range("\u00D8-\u00F6"), range("\u00F8-\u02FF"), range("\u0370-\u037D"), range("\u037F-\u1FFF"), range("\u200C-\u200D"), range("\u2070-\u218F"), range("\u2C00-\u2FEF"), range("\u3001-\uD7FF"), range("\uF900-\uFDCF"), range("\uFDF0-\uFFFD")/*, range("\u10000-\uEFFFF")*/));
//        Matcher NAME_PART       = syntax.add("NAME_PART",      or(NAME_START, ch('-'), ch('.'), DIGIT,  ch('\u00B7'), range("\u0300-\u036F"), range("\u203F-\u2040")));
//        Matcher NCNAME_START    = syntax.add("NCNAME_START",   minus(NAME_START, ch(':')));
//        Matcher NCNAME_PART     = syntax.add("NCNAME_PART",    minus(NAME_PART, ch(':')));

//        Rule rule = new Rule();
//        rule.node = new Node();
//        syntax.add("Rule1", rule);

        NBLREditor editor = new NBLREditor(syntax);
        editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        editor.setVisible(true);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy