IconManager.java :  » UnTagged » mosync » com » mobilesorcery » sdk » core » Android Open Source

Android Open Source » UnTagged » mosync 
mosync » com » mobilesorcery » sdk » core » IconManager.java
/*  Copyright (C) 2009 Mobile Sorcery AB

    This program is free software; you can redistribute it and/or modify it
    under the terms of the Eclipse Public License v1.0.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for
    more details.

    You should have received a copy of the Eclipse Public License v1.0 along
    with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html
*/
package com.mobilesorcery.sdk.core;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

/**
 * A simple class for handling icon and their injection for Linux 
 * platforms. At the time this was written, the icon-injector tool
 * in the MoSync source tree did have the required behavior for
 * use on Linux platforms, thus this class was written. 
 * Note: This class is dependent on the eternal tool ImageMagick.
 * 
 * @author Ali Mosavian 
 */
public class IconManager 
{
  /**
   * Internal class that is used to keep track of icons
   * 
   * @author Ali Mosavian
   */
  static class Icon 
  {
    private  int  m_width;
    private  int  m_height;
    private File m_path;
    
    /**
     * Constructor
     * 
     * @param w width
     * @param h height
     * @param p Path to icon 
     */
    public Icon ( int w, 
            int h, 
            String p )
    {
      m_width = w;
      m_height= h;
      m_path  = new File( p );      
    }
    
    
    /**
     * Score this icon against the requested size. 
     * The algorithm scoring algorithm works as such,
     *  - If the size are equal, then Integer.MAX_VALUE is returned
     *  
     *  - If this icon is larger, a positive score is returned. 
     *    The larger it is, the higher the score is.
     *    
     *  - If this icon is smaller (on any side), a negative score is 
     *    returned. The smaller it is, the less the score will be.
     * 
     * @param w Requested width
     * @param h Requested height
     * 
     * @return Score, higher score means better fit
     */
    public int getScore ( int w,
                   int h )
    {
      int dw = w-m_width;
      int dh = h-m_height;
      int score2 = Math.abs( dw )+Math.abs( dh );
      
      if ( dw < 0 || dh < 0 )
        score2 = -score2;
      
      return (score2 == 0 ? Integer.MAX_VALUE : score2);
    }
    
    /**
     * Returns File instance of icon
     * 
     * @return File object
     */
    public File getFile ( )
    {
      return m_path;
    }
    
    /**
     * Checks if this is SVG icon
     * 
     */
    public boolean isSVG ( )
    {
      return m_path.getName( ).endsWith( ".svg" );
    }
  }  
    
  private Map<String, List<Icon>>  m_iconMap;
  private DefaultPackager      m_internal;
  
  
  /**
   * Constructor, will go through the project directory for any
   * files ending with ".icon" and parse them.
   * 
   * @param p Used to execute commands
   * @param b Project base directory
   * 
   * @throws Exception 
   * @throws CoreException 
   */
  public IconManager ( DefaultPackager p,
             MoSyncProject b )
  throws Exception, CoreException
  {
    m_internal = p;
    m_iconMap  = new HashMap<String, List<Icon>>( );
    
    File iconXML = b.getIconFile();
    
    if(null != iconXML) loadIconMetaData(iconXML);

  }
  
  /**
   * Returns wether or not an icon type exists
   * 
   * @param type Icon type, either "svg" of some bitmap format
   *          such as PNG.
   * 
   * @return True on success, false otherwise
   */
  public boolean hasIcon ( String type )
  {
    if ( type.equals( "svg" ) == true && 
       m_iconMap.containsKey( "vector" ) == false )
      return false;
    
    else if ( m_iconMap.containsKey( "bitmap" ) == false )
      return false;
    
    
    return true;
  }  
  
  /**
   * Will "inject", that is copy or convert the best fitting 
   * icon to the output file. Conversion is done with the
   * ImageMagick tool which is assumed to be in %mosync-bin%.
   * 
   * @param o The file where the icon will be written to.   * 
   * @param w The width of the output icon
   * @param h The height of the output icon 
   * @param type Output type, either "svg" of some bitmap format
   *          such as PNG.
   * 
   * @return True on success, false otherwise
   * @throws Exception If file copying fails, for instance.
   */
  public boolean inject ( File o,
              int w,
              int h,
              String type ) 
  throws Exception
  {
    Icon ico;
    char sep = File.separatorChar;
    
    // Create a console which can be used for debug messaging to the Eclispe console
    IProcessConsole console = CoreMoSyncPlugin.getDefault().createConsole(MoSyncBuilder.CONSOLE_ID);
    
    // Can't do SVG output without source SVG
    if ( type.equals( "svg" ) == true )
    {
      console.addMessage("injecting svg icon");
      
       if ( m_iconMap.containsKey( "vector" ) == false )
         return false;
       
       ico = m_iconMap.get( "vector" ).get( 0 );       

       if ( ico.getFile( ).exists( ) == false )
         return false;
       Util.copyFile( new NullProgressMonitor( ), ico.getFile( ), o );      
    }
    else
    {
      console.addMessage("injecting bitmap icon!");
      
      ico = findBestMatch( w, h );
      
      if(null == ico) console.addMessage("no icon retrived!");
      
      // Check if size is the same
      if ( ico.getScore( w, h ) == Integer.MAX_VALUE )
      {
        console.addMessage("using unconverted icon :" + ico.getFile( ).getAbsolutePath( ));
        Util.copyFile( new NullProgressMonitor( ), ico.getFile( ), o );
      }
      else
      {  
        // Convert
        console.addMessage("converting icon :" + ico.getFile( ).getAbsolutePath( ));
        
        String bin = MoSyncTool.getDefault( ).getBinary( "ImageMagick/convert" ).toOSString( );
        if ( m_internal.runCommandLineWithRes( bin,
                                ico.getFile( ).getAbsolutePath( ), 
                                "-resize", w + "x" + h,
                                o.getAbsolutePath( ) ) != 0 )
          return false;
      }
    }
    
    
    return true;
  }
  
  
  /**
   * This method finds the best matching bitmap icon for the 
   * requested with and height. The resulting icon does not
   * have to be the same size or even the same format as you
   * think it is. So make sure you convert it to the right 
   * size and format. 
   * 
   * @param w The requested with
   * @param h The requested height.
   * 
   * @return A bitmap icon which is the closest to the requested
   *        resolution.
   * 
   * @throws Exception If there aren't any icons at all.
   */
  private Icon findBestMatch ( int w, int h )
  throws Exception
  {
    Icon icon = null;
    int score = Integer.MIN_VALUE;
    
    // SVG always has preference
    /*
     * Unfortunately, ImageMagick doesn't seem to have proper 
     * SVG support, so although this would have been ideal,
     * it can not be used.
     * 
    if ( m_iconMap.containsKey( "vector" ) )
      return m_iconMap.get( "vector" ).get( 0 );
    */
        
    if (  m_iconMap.containsKey( "bitmap" ) == false )
      throw new Exception( "No icons" );
    
    for ( Icon i : m_iconMap.get( "bitmap" ) )
    {
      if ( i.getScore( w, h ) < score )
        continue;
      
      icon  = i;
      score = i.getScore( w, h );        
    }
    
    return icon;
  }
  
  /**
   * Loads icon meta data from XML
   * Note: It is assumed that the input XML(s) have the following
   *      XML format
   * 
   *      <icon>
   *       <instance src="someicon.svg"/>
   *       <instance src="someicon1.png" size="w1xh1"/>
   *       <instance src="someicon2.bmp" size="w2xh2"/>
   *       .
   *       .
   *       .
   *       <instance src="someiconN.jpg" size="wNxhN"/>
   *      </icon>
   * 
   *     If multiple SVG files are defined, the first one will
   *     always be chosen. For bitmap formats ( non-vector ) 
   *     that is, the one that is closest in size to the output
   *     will be chosen for conversion.
   * 
   * @param f XML file
   * 
   * @throws Exception Occurs when the XML is badly formated.
   */
  private void loadIconMetaData ( File f )
  throws Exception
  {
    SAXParser       saxParse;
    XMLHandler       iconParser = new XMLHandler( );
    SAXParserFactory  saxFact = SAXParserFactory.newInstance( );
        
    try 
    {      
      saxParse = saxFact.newSAXParser( );
      saxParse.parse( f, iconParser );
    } 
    catch ( Exception e ) 
    {
      throw new Exception( "Failed to parse icon xml", e );
    }
  
    List<Icon> bitmapList = new LinkedList<Icon>( );
    
    // Go through icons
    for ( Entry<String, Entry<Integer, Integer>> e : iconParser.getIconList( ) )
    {  
      int       wdth = e.getValue( ).getKey( );
      int       hght = e.getValue( ).getValue( );
      String  path = f.getParent( ) + File.separatorChar + e.getKey( );      
      Icon   icon = new Icon( wdth, hght, path );
      
      if ( icon.isSVG( ) == true )
      {
        List<Icon> l = new LinkedList<Icon>( );
        l.add( icon );
        m_iconMap.put("svg", l);
      }
      else bitmapList.add( icon );
    }
    
    m_iconMap.put("bitmap", bitmapList);
      
  }
}


/**
 * SAX event handler for parsing icon meta XML
 * 
 * @author Ali Mosavian
 *
 */
class XMLHandler 
extends DefaultHandler
{  
  Stack<String> m_parseStack;
  List<Entry<String, Entry<Integer, Integer>>> m_iconList;
  
  
  /**
   * Constructor
   * Inits parse stack and other internal data structures.
   * 
   */
  public XMLHandler ( )
  {
    m_parseStack = new Stack<String>( );
    m_iconList   = new LinkedList<Entry<String, Entry<Integer, Integer>>>( );    
  }  
  
  
  /**
   * Invoked by SAX parser at the start of an XML tag
   * 
   * @param uri The Namespace URI, or the empty string if the element 
   *         has no Namespace URI or if Namespace processing is not 
   *         being performed.
   * 
   * @param localName The local name (without prefix), or the empty string 
   *             if Namespace processing is not being performed.
   * 
   * @param qName The qualified name (with prefix), or the empty string if 
   *         qualified names are not available.
   * 
   * @param attributes The attributes attached to the element. If there are 
   *            no attributes, it shall be an empty Attributes object.
   * 
   * @exception RuntimeException Occurs if the XML isn't properly formated.
   */
  public void startElement( String uri, 
                String localName, 
                String qName, 
                Attributes atts )
  {    
    // New icon ?
    if ( qName.equals( "instance" ) == true )
    {
      if ( m_parseStack.empty( ) == false && 
         m_parseStack.peek( ).equals( "icon" ) == true  )
      {
        int  w = 0;
        int h = 0;
        String size = "";
        String path = "";
        Entry<Integer, Integer> iconSize;        
        
        // Parse attributes
        for ( int i = 0; i < atts.getLength( ); i++ )
        {
          String n = atts.getQName( i );
          if ( n.equals( "size" ) == true )
            size = atts.getValue( i );
          else if ( n.equals( "src" ) == true )
            path = atts.getValue( i );            
        }
        
        // Semantic check
        if ( path.isEmpty( ) == true )
          throw new RuntimeException( "Badly formated icon xml: no src" );
        
        if ( size.isEmpty( ) == true && 
           path.endsWith( ".svg" ) == false )
          throw new RuntimeException( "Badly formated icon xml: src, but no size" );
        
        // Parse size
        if ( size.isEmpty( ) == false )
        {
          if ( size.equalsIgnoreCase( "default" ) == false )
          {
            String[] tmp = size.split( "x" );        
            if ( tmp.length != 2 )
              throw new RuntimeException( "Badly formated icon xml: bad size format" );
            
            try {
              w = Integer.parseInt( tmp[0] );
              h = Integer.parseInt( tmp[1] );          
            } catch ( NumberFormatException e ) {
              throw new RuntimeException( "Badly formated icon xml, size attribute", e );
            }          
          }
        }

        // Add icon to list
        iconSize = new SimpleEntry<Integer, Integer>( w, h );
        m_iconList.add( new SimpleEntry<String, Entry<Integer,Integer>>( path, iconSize ) );
      }        
    }

    m_parseStack.push( qName );
  }

  
  /**
   * Invoked by SAX parser at the end of an XML tag.
   * 
   * @param uri The Namespace URI, or the empty string if the element 
   *         has no Namespace URI or if Namespace processing is not 
   *         being performed.
   * 
   * @param localName The local name (without prefix), or the empty string 
   *             if Namespace processing is not being performed.
   * 
   * @param qName The qualified name (with prefix), or the empty string if 
   *         qualified names are not available. 
   */
  public void endElement ( String uri, 
               String localName, 
               String qName )
  {
    if ( m_parseStack.empty( ) == true )
      return;

    if ( m_parseStack.peek( ).equals( qName ) == true )
      m_parseStack.pop( );
  }
  
  
  /**
   * Returns icon list
   * 
   * @return Every entry is <Path, <Width, Height>>
   */
  public List<Entry<String, Entry<Integer, Integer>>> getIconList ( )
  {
    return m_iconList;
  }
  
    
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.