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

editor.GosuStyleContext Maven / Gradle / Ivy

There is a newer version: 1.18.1
Show newest version
/*
 *
 *  Copyright 2010 Guidewire Software, Inc.
 *
 */
package editor;

import editor.util.EditorUtilities;
import editor.util.Pair;
import gw.lang.parser.ISourceCodeTokenizer;
import gw.lang.parser.exceptions.ParseWarningForDeprecatedMember;
import gw.util.GosuObjectUtil;

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.TabExpander;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.WrappedPlainView;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A collection of styles used to render gosu source.
 */
public class GosuStyleContext extends StyleContext implements ViewFactory
{
  public static final int KEY_WORD = -99;
  public static final int PARSE_ERROR = -98;
  public static final int DEPRECATED = -97;
  public static final int PARSE_WARNING = -96;
  public static final int TYPE_LITERAL = -95;
  public static final int NESTED_TYPE_LITERAL = -94;
  public static final int METHOD_CALL = -93;
  public static final int USES = -92;
  public static final int DEFAULT = -91;
  public static final int FIELD_ERROR = -90;
  public static final int FIELD_WARNING = -89;
  public static final int PACKAGE = -88;
  public static final int PROPERTY = -87;
  public static final int ENHANCEMENT_METHOD_CALL = -86;
  public static final int ENHANCEMENT_PROPERTY = -85;

  public static final Integer KEY_WORD_KEY = new Integer( KEY_WORD );
  public static final Integer PARSE_ERROR_KEY = new Integer( PARSE_ERROR );
  public static final Integer DEPRECATED_KEY = new Integer( DEPRECATED );
  public static final Integer TYPE_LITERAL_KEY = new Integer( TYPE_LITERAL );
  public static final Integer NESTED_TYPE_LITERAL_KEY = new Integer( NESTED_TYPE_LITERAL );
  public static final Integer PARSE_WARNING_KEY = new Integer( PARSE_WARNING );
  public static final Integer METHOD_CALL_KEY = new Integer( METHOD_CALL );
  public static final Integer PROPERTY_KEY = new Integer( PROPERTY );
  public static final Integer ENHANCEMENT_METHOD_CALL_KEY = new Integer( ENHANCEMENT_METHOD_CALL );
  public static final Integer ENHANCEMENT_PROPERTY_KEY = new Integer( ENHANCEMENT_PROPERTY );
  public static final Integer USES_KEY = new Integer( USES );
  public static final Integer PACKAGE_KEY = new Integer( PACKAGE );
  public static final Integer FIELD_ERROR_KEY = new Integer( FIELD_ERROR );
  public static final Integer FIELD_WARNING_KEY = new Integer( FIELD_WARNING );
  public static final String DASHED = "_dashed";
  private static final Component THISISSTUPID = new Component()
  {
  };
  //private static String g_defFontFamily =  EditorUtilities.getFontFamilyOrDefault( "Consolas", "Monospaced" );
  private static String g_defFontFamily =  EditorUtilities.getFontFamilyOrDefault( "Monospaced", "Monospaced" );
  private static int g_defFontSize = 12;

  public static final String STYLE_EOL = "EOL";
  public static final String STYLE_EOF = "EOF";
  public static final String STYLE_Whitespace = "Whitespace";
  public static final String STYLE_Comment = "Comment";
  public static final String STYLE_Caret = "Caret";
  public static final String STYLE_Number = "Number";
  public static final String STYLE_Integer = "Integer";
  public static final String STYLE_Word = "Word";
  public static final String STYLE_Operator = "Operator";
  public static final String STYLE_StringLiteral = "StringLiteral";
  public static final String STYLE_KeyWord = "KeyWord";
  public static final String STYLE_ParseError = "ParseError";
  public static final String STYLE_ParseWarning = "ParseWarning";
  public static final String STYLE_DeprecatedMember = "DeprecatedMember";
  public static final String STYLE_TypeLiteral = "ITypeLiteralExpression";
  public static final String STYLE_NestedTypeLiteral = "NestedTypeLiteral";
  public static final String STYLE_MethodCall = "MethodCall";
  public static final String STYLE_Property = "Property";
  public static final String STYLE_EnhancementMethodCall = "EnhancementMethodCall";
  public static final String STYLE_EnhancementProperty = "EnhancementProperty";
  public static final String STYLE_FieldError = "FieldError";
  public static final String STYLE_FieldWarning = "FieldWarning";

  private String _strFontFamily;
  private int _iFontSize;
  private HashMap _fontCache;
  private HashMap _tokenStyles;

  /**
   * Constructs a set of styles to represent gosu lexical tokens.
   */
  public GosuStyleContext()
  {
    super();
    Style root = getStyle( DEFAULT_STYLE );
    _tokenStyles = new HashMap<>();

    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_EOL ), addStyle( STYLE_EOL, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_EOF ), addStyle( STYLE_EOF, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_WHITESPACE ), addStyle( STYLE_Whitespace, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_COMMENT ), addStyle( STYLE_Comment, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_NUMBER ), addStyle( STYLE_Number, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_INTEGER ), addStyle( STYLE_Integer, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_WORD ), addStyle( STYLE_Word, root ) );
    _tokenStyles.put( new Integer( ISourceCodeTokenizer.TT_OPERATOR ), addStyle( STYLE_Operator, root ) );
    _tokenStyles.put( new Integer( (int)'"' ), addStyle( STYLE_StringLiteral, root ) );
    _tokenStyles.put( (int)'\'', addStyle( STYLE_StringLiteral, root ) );
    _tokenStyles.put( KEY_WORD_KEY, addStyle( STYLE_KeyWord, root ) );
    _tokenStyles.put( PARSE_ERROR_KEY, addStyle( STYLE_ParseError, root ) );
    _tokenStyles.put( PARSE_WARNING_KEY, addStyle( STYLE_ParseWarning, root ) );
    _tokenStyles.put( DEPRECATED_KEY, addStyle( STYLE_DeprecatedMember, root ) );
    _tokenStyles.put( TYPE_LITERAL_KEY, addStyle( STYLE_TypeLiteral, root ) );
    _tokenStyles.put( NESTED_TYPE_LITERAL_KEY, addStyle( STYLE_NestedTypeLiteral, root ) );
    _tokenStyles.put( METHOD_CALL_KEY, addStyle( STYLE_MethodCall, root ) );
    _tokenStyles.put( PROPERTY_KEY, addStyle( STYLE_Property, root ) );
    _tokenStyles.put( ENHANCEMENT_METHOD_CALL_KEY, addStyle( STYLE_EnhancementMethodCall, root ) );
    _tokenStyles.put( ENHANCEMENT_PROPERTY_KEY, addStyle( STYLE_EnhancementProperty, root ) );
    _tokenStyles.put( FIELD_ERROR_KEY, addStyle( STYLE_FieldError, root ) );
    _tokenStyles.put( FIELD_WARNING_KEY, addStyle( STYLE_FieldWarning, root ) );

    addStyle( STYLE_Caret, root );

    _strFontFamily = g_defFontFamily;
    _iFontSize = g_defFontSize;
    _fontCache = new HashMap<>();

    setDefaultStyles();
  }

  public void setForeground( Style style, Color colorFore )
  {
    setAttribute( style, StyleConstants.Foreground, colorFore );
  }

  public void setBackground( Style style, Color colorBack )
  {
    setAttribute( style, StyleConstants.Background, colorBack );
  }

  public void setBold( Style style, boolean bBold )
  {
    setAttribute( style, StyleConstants.Bold, bBold );
  }

  public void setItalic( Style style, boolean bItalic )
  {
    setAttribute( style, StyleConstants.Italic, bItalic );
  }

  public void setUnderline( Style style, boolean bUnderline )
  {
    setAttribute( style, StyleConstants.Underline, bUnderline );
  }

  public void setStrikeThrough( Style style, boolean bStrikeThrough )
  {
    setAttribute( style, StyleConstants.StrikeThrough, bStrikeThrough );
  }

  public void setAttribute( Style style, Object attr, Object value )
  {
    Style defStyle = getStyle( DEFAULT_STYLE );
    if( defStyle == style )
    {
      style.addAttribute( attr, value );
      for( Enumeration names = getStyleNames(); names.hasMoreElements(); )
      {
        Style csr = getStyle( (String)names.nextElement() );
        if( csr != defStyle &&
            GosuObjectUtil.equals( csr.getAttribute( attr ), value ) )
        {
          // Remove attr for child attrs having same value
          csr.removeAttribute( attr );
        }
      }
      return;
    }

    Object defValue = defStyle.getAttribute( attr );
    if( GosuObjectUtil.equals( defValue, value ) )
    {
      style.removeAttribute( attr );
    }
    else
    {
      style.addAttribute( attr, value );
    }
  }

  public void setDefaultStyles()
  {
    boolean bAllowBold = true;

    // Default
    Style style = getStyle( DEFAULT_STYLE );
    setBackground( style, Scheme.active().getCodeWindow() );
    setForeground( style, Scheme.active().getCodeWindowText() );

    // Caret
    // style = getStyle( STYLE_Caret );

    // Whitespace
    // style = getStyleForScanValue( SourceCodeTokenizer.TT_WHITESPACE );

    // Comments
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_COMMENT );
    setForeground( style, Scheme.active().getCodeComment()  );
    setItalic( style, true );

    // EOL (same as Comment... to handle multiline comments)
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_EOL );
    setForeground( style, Scheme.active().getCodeMultilineComment() );
    setItalic( style, true );

    // EOF (same as Comment... to handle multiline comments)
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_EOF );
    setForeground( style, Scheme.active().getCodeMultilineComment() );
    setItalic( style, true );

    // String Literals
    style = getStyleForScanValue( (int)'"' );
    setForeground( style, Scheme.active().getCodeStringLiteral() );
    setBold( style, bAllowBold );

    // Number Literals
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_NUMBER );
    setForeground( style, Scheme.active().getCodeNumberLiteral() );

    // Integer Literals
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_INTEGER );
    setForeground( style, Scheme.active().getCodeNumberLiteral() );

    // Non-key Words (identifiers and bean member access paths)
    // style = getStyleForScanValue( ISourceCodeTokenizer.TT_WORD );

    // Key Words
    style = getStyleForScanValue( GosuStyleContext.KEY_WORD );
    setForeground( style, Scheme.active().getCodeKeyword() );
    setBold( style, bAllowBold );

    // Parse Errors
    style = getStyleForScanValue( GosuStyleContext.PARSE_ERROR );
    setForeground( style, Scheme.active().getCodeError() );

    // Parse Warnings
    style = getStyleForScanValue( GosuStyleContext.PARSE_WARNING );
    setForeground( style, Scheme.active().getCodeWarning() );
    setUnderline( style, true );

    // Deprecated Member
    style = getStyleForScanValue( GosuStyleContext.DEPRECATED );
    setForeground( style, Scheme.active().getCodeDeprecated() );
    setStrikeThrough( style, true );

    // Operators
    style = getStyleForScanValue( ISourceCodeTokenizer.TT_OPERATOR );
    setForeground( style, Scheme.active().getCodeOperator() );
    setBold( style, bAllowBold );

    // Type Literals
    style = getStyleForScanValue( GosuStyleContext.TYPE_LITERAL );
    setForeground( style, Scheme.active().getCodeTypeLiteral() );
    setBold( style, bAllowBold );

    // Type Literals (namespaces and type-parameters)
    style = getStyleForScanValue( GosuStyleContext.NESTED_TYPE_LITERAL );
    setForeground( style, Scheme.active().getCodeTypeLiteralNested() );

    // Type Literals (namespaces and type-parameters)
    style = getStyleForScanValue( GosuStyleContext.FIELD_ERROR );
    setBackground( style, new Color( 255, 230, 230 ) );

    // Type Literals (namespaces and type-parameters)
    style = getStyleForScanValue( GosuStyleContext.FIELD_WARNING );
    setBackground( style, new Color( 254, 251, 94 ) );
  }

  public static void setDefaultFontFamily( String defFontFamily )
  {
    g_defFontFamily = defFontFamily;
  }

  public static String getDefaultFontFamily()
  {
    return g_defFontFamily;
  }

  public static void setDefaultFontSize( int defFontSize )
  {
    g_defFontSize = defFontSize;
  }

  public static int getDefaultFontSize()
  {
    return g_defFontSize;
  }

  /**
   * Fetch the foreground color to use for a lexical token with the given value.
   */
  public Color getForeground( int code )
  {
    Style s = _tokenStyles.get( new Integer( code ) );
    if( s == null )
    {
      s = getStyle( DEFAULT_STYLE );
    }
    return getForeground( s );
  }

  public Color getBackground( int code )
  {
    Style s = _tokenStyles.get( new Integer( code ) );
    if( s == null )
    {
      s = getStyle( DEFAULT_STYLE );
    }
    return getBackground( s );
  }

  /**
   * Fetch the font to use for a lexical token with the given scan value.
   */
  public Font getFont( int code )
  {
    Style s = _tokenStyles.get( new Integer( code ) );
    if( s == null )
    {
      s = getStyle( DEFAULT_STYLE );
    }
    return getFont( s );
  }

  /**
   * Fetches the attribute set to use for the given scan code.  The set is
   * stored in a table to facilitate relatively fast access to use in
   * conjunction with the scanner.
   */
  public Style getStyleForScanValue( int code )
  {
    Style s = _tokenStyles.get( new Integer( code ) );
    if( s == null )
    {
      s = getStyle( DEFAULT_STYLE );
    }
    return s;
  }

  /**
   * Fetch the font to use for a given attribute set.
   */
  @Override
  public Font getFont( AttributeSet attr )
  {
    boolean bUnderline = StyleConstants.isUnderline( attr );
    boolean bStrikethrough = StyleConstants.isStrikeThrough( attr );

    if( !bUnderline && !bStrikethrough )
    {
      // StyleContext ignores the Underline and Strikethrough attribute
      return getFont( attr, getFontFamily( attr ) );
    }

    // Must build the font via TextAttribute map to support Underlined and Strikethrough text

    Map map = new HashMap();
    map.put( TextAttribute.FAMILY, getFontFamily( attr ) );
    map.put( TextAttribute.SIZE, (float)getFontSize() );
    map.put( TextAttribute.WEIGHT, StyleConstants.isBold( attr ) ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR );
    map.put( TextAttribute.POSTURE, StyleConstants.isItalic( attr ) ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR );
    if( bUnderline )
    {
      map.put( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON );
      if( attr.getAttribute( DASHED ) != null )
      {
        map.put( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_GRAY );
      }
    }
    if( bStrikethrough )
    {
      map.put( TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON );
    }

    Font font = _fontCache.get( attr );
    if( font == null )
    {
      font = new Font( map );
      _fontCache.put( attr, font );
    }

    return font;
  }

  private Font getFont( AttributeSet attr, String strFamily )
  {
    int iStyle = Font.PLAIN;
    if( StyleConstants.isBold( attr ) )
    {
      iStyle |= Font.BOLD;
    }
    if( StyleConstants.isItalic( attr ) )
    {
      iStyle |= Font.ITALIC;
    }
    int iSize = getFontSize();

    // If either superscript or subscript is is set, we need to reduce the font
    // size by 2.
    if( StyleConstants.isSuperscript( attr ) ||
        StyleConstants.isSubscript( attr ) )
    {
      iSize -= 2;
    }

    return getFont( strFamily, iStyle, iSize );
  }

  public String getFontFamily( AttributeSet a )
  {
    String strFamily = (String)a.getAttribute( StyleConstants.FontFamily );
    if( strFamily == null )
    {
      strFamily = getFontFamily();
    }
    return strFamily;
  }

  public String getFontFamily()
  {
    return _strFontFamily;
  }

  public void setFontFamily( String strFamily )
  {
    _strFontFamily = strFamily;
    _fontCache.clear();
  }

  public int getFontSize()
  {
    return _iFontSize;
  }

  public void setFontSize( int iSize )
  {
    _iFontSize = iSize;
    _fontCache.clear();
  }

  //----------------------------------------------------------------------------------------------
  //-- ViewFactory methods --

  @Override
  public View create( Element elem )
  {
    return new GosuSourceView( elem );
  }

  public List