/**
* Copyright 2010-2014 Three Crickets LLC.
* <p>
* The contents of this file are subject to the terms of a BSD license. See
* attached license.txt.
* <p>
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly from Three Crickets
* at http://threecrickets.com/
*/
package org.sikuli.syntaxhighlight.style;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.sikuli.syntaxhighlight.Jygments;
import org.sikuli.syntaxhighlight.NestedDef;
import org.sikuli.syntaxhighlight.ResolutionException;
import org.sikuli.syntaxhighlight.Util;
import org.sikuli.syntaxhighlight.grammar.TokenType;
import org.sikuli.syntaxhighlight.style.def.StyleElementDef;
/**
* @author Tal Liron
*/
public class Style extends NestedDef<Style>
{
static String extJSON = ".jso";
//
// Static operations
//
public static Style getByName( String name ) throws ResolutionException
{
if( Character.isLowerCase( name.charAt( 0 ) ) )
name = Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ) + "Style";
Style style = getByFullName( name );
if( style != null )
return style;
else
{
// Try contrib package
String pack = Jygments.class.getPackage().getName();
return getByFullName( pack, "contrib", name );
}
}
public static Style getByFullName( String name ) throws ResolutionException {
return getByFullName("", "", name);
}
@SuppressWarnings("unchecked")
public static Style getByFullName( String pack, String sub, String name ) throws ResolutionException
{
String fullname = name;
if (!pack.isEmpty()) {
if (!sub.isEmpty()) {
fullname = pack + "." + sub + "." + fullname;
} else {
fullname = pack + "." + fullname;
}
}
// Try cache
Style style = styles.get( fullname );
if( style != null )
return style;
try
{
return (Style) Jygments.class.getClassLoader().loadClass( fullname ).newInstance();
}
catch( InstantiationException x )
{
}
catch( IllegalAccessException x )
{
}
catch( ClassNotFoundException x )
{
}
InputStream stream = Util.getJsonFile(pack, sub, name, fullname);
if( stream != null )
{
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.getFactory().configure( JsonParser.Feature.ALLOW_COMMENTS, true );
try
{
Map<String, Object> json = objectMapper.readValue( stream, HashMap.class );
style = new Style();
style.addJson( json );
style.resolve();
// Cache it
Style existing = styles.putIfAbsent( fullname, style );
if( existing != null )
style = existing;
return style;
}
catch( JsonParseException x )
{
throw new ResolutionException( x );
}
catch( JsonMappingException x )
{
throw new ResolutionException( x );
}
catch( IOException x )
{
throw new ResolutionException( x );
}
}
return null;
}
//
// Attributes
//
public Map<TokenType, List<StyleElement>> getStyleElements()
{
return styleElements;
}
//
// Operations
//
public void addStyleElement( TokenType tokenType, StyleElement styleElement )
{
List<StyleElement> styleElementsForTokenType = styleElements.get( tokenType );
if( styleElementsForTokenType == null )
{
styleElementsForTokenType = new ArrayList<StyleElement>();
styleElements.put( tokenType, styleElementsForTokenType );
}
styleElementsForTokenType.add( styleElement );
}
public void resolve() throws ResolutionException
{
resolve( this );
}
//
// Def
//
@Override
public boolean resolve( Style style ) throws ResolutionException
{
if( super.resolve( style ) )
{
boolean done = false;
while( !done )
{
done = true;
for( TokenType tokenType : TokenType.getTokenTypes() )
{
if( tokenType != TokenType.Token )
{
if( !styleElements.containsKey( tokenType ) )
{
boolean doneOne = false;
TokenType parent = tokenType.getParent();
while( parent != null )
{
if( parent == TokenType.Token )
{
doneOne = true;
break;
}
List<StyleElement> parentElements = styleElements.get( parent );
if( parentElements != null )
{
styleElements.put( tokenType, parentElements );
doneOne = true;
break;
}
parent = parent.getParent();
}
if( !doneOne )
done = false;
}
}
}
}
return true;
}
else
return false;
}
// //////////////////////////////////////////////////////////////////////////
// Protected
protected void add( String tokenTypeName, String... styleElementNames )
{
ArrayList<String> list = new ArrayList<String>( styleElementNames.length );
for( String styleElementName : styleElementNames )
list.add( styleElementName );
addDef( new StyleElementDef( tokenTypeName, list ) );
}
@SuppressWarnings("unchecked")
protected void addJson( Map<String, Object> json ) throws ResolutionException
{
for( Map.Entry<String, Object> entry : json.entrySet() )
{
String tokenTypeName = entry.getKey();
if( entry.getValue() instanceof Iterable<?> )
{
for( String styleElementName : (Iterable<String>) entry.getValue() )
add( tokenTypeName, styleElementName );
}
else if( entry.getValue() instanceof String )
add( tokenTypeName, (String) entry.getValue() );
else
throw new ResolutionException( "Unexpected value in style definition: " + entry.getValue() );
}
}
// //////////////////////////////////////////////////////////////////////////
// Private
private static final ConcurrentMap<String, Style> styles = new ConcurrentHashMap<String, Style>();
private final Map<TokenType, List<StyleElement>> styleElements = new HashMap<TokenType, List<StyleElement>>();
}