/* * Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved. * * This program and the accompanying materials are made available * under the terms of the Eclipse Public License, Version 1.0, * which accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html * */ package net.rim.ejde.internal.ui.editors.key; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.Vector; import net.rim.ejde.internal.model.BlackBerryProperties; import net.rim.ejde.internal.util.Messages; import net.rim.ejde.internal.util.ProjectUtils; import net.rim.ide.JavaParser; import net.rim.ide.core.ObjectCounter; import net.rim.ide.core.Util; import org.apache.log4j.Logger; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.osgi.util.NLS; /** * a base class to provide support for creating the workspace/package protection dialog */ public class CodeSigningState { private static final Logger _logger = Logger.getLogger( CodeSigningState.class ); private static final String NAME_PUBLIC = ".public"; private static final String NAME_NON_PUBLIC = ".nonpublic"; private static final String NAME_NO_KEY = "nokey"; private String _textPublic; private String _textNonPublic; private List< ACheck > _allChecks; private String _textNoKey; // package name to key name mapping private Hashtable< String, String > _temporaryPackageProtection; // class name to key name mapping private Hashtable< String, String > _temporaryClassProtection; // package name to key name mapping private Hashtable< String, String > _allPackages; // class name to key name mapping private Hashtable< String, String > _allClasses; private String _currentKeyFile; private Hashtable< String, String > _keyTitles; private Vector< String > _keys; private boolean _change; private TreeNode _tree; private List< CheckBox > _packageChecks; private List< CheckBox > _classChecks; private Callback _cb; private IProject _project; private BlackBerryProperties _bbProperties; /** * an abstract check box implementation */ public interface CheckBox { public boolean isEnabled(); public void setEnabled( boolean on ); public boolean isSelected(); public void setSelected( boolean on ); public String getText(); public void setText( String text ); } /** * an abstract tree node implementation */ public interface TreeNode { public void setText( Object o ); public void addChild( TreeNode child ); } public class OneClass { private String _name; private CheckBox _check; private boolean _isPublic; OneClass( String name, boolean isPublic ) { _name = name; _isPublic = isPublic; } public String toString() { return ( _isPublic ? "public " : "" ) + _name; } String getName() { return _name; } boolean isPublic() { return _isPublic; } CheckBox getCheck() { return _check; } void setCheck( CheckBox check ) { _check = check; } } public class OnePackage { private String _name; private String _displayName; private Vector< OneClass > _classes; private CheckBox _check; OnePackage( String name, String displayName ) { _displayName = displayName; _name = name; _classes = new Vector< OneClass >(); } OnePackage( String name ) { this( name, name ); } String getName() { return _name; } void addClass( OneClass cls ) { _classes.addElement( cls ); } OneClass getClass( int i ) { return _classes.elementAt( i ); } int getNumClasses() { return _classes.size(); } void sortClasses() { Util.stringSort( _classes, false ); } public String toString() { return _name; } String getDisplayName() { return _displayName; } CheckBox getCheck() { return _check; } void setCheck( CheckBox check ) { _check = check; } } private class ACheck { private ACheck _parent; private CheckBox _check; // package/class name to key mapping private Hashtable< String, String > _table; // package/class name private String _tableKey; private String _displayName; private TreeNode _node; private boolean _isDefaultProtection; ACheck( CheckBox check, ACheck parent, TreeNode node, Hashtable< String, String > table, String tableKey, String displayName ) { _parent = parent; _check = check; _table = table; _tableKey = tableKey; _displayName = displayName; _node = node; String key = _table.get( _tableKey ); _check.setSelected( key != null && key.equals( _currentKeyFile ) ); _check.setText( _displayName ); if( tableKey.equals( NAME_PUBLIC ) || tableKey.equals( NAME_NON_PUBLIC ) ) { _isDefaultProtection = true; } } void clear() { if( _table.remove( _tableKey ) != null ) { _change = true; } _check.setSelected( false ); _check.setEnabled( true ); _check.setText( _displayName ); _cb.nodeChanged( _node ); } void updateCheck( boolean force ) { String parentKey = null; if( _parent != null ) { _parent.updateCheck( force ); parentKey = _parent._table.get( _parent._tableKey ); } String oldText = _check.getText(); boolean wasEnabled = _check.isEnabled(); String key = _table.get( _tableKey ); if( key != null ) { if( key.equals( _currentKeyFile ) ) { _check.setText( _displayName ); _check.setEnabled( true ); _check.setSelected( true ); } else { _check.setText( NLS.bind( Messages.PrivateKeyEditor_ProtectOtherKey, _displayName, key ) ); _check.setEnabled( false ); _check.setSelected( false ); } } else { if( parentKey != null ) { _check.setText( NLS.bind( Messages.PrivateKeyEditor_ProtectOtherKey, _displayName, parentKey ) ); } else { _check.setText( _displayName ); } _check.setEnabled( true ); _check.setSelected( false ); } if( _isDefaultProtection && _currentKeyFile.equals( NAME_NO_KEY ) ) { _check.setEnabled( false ); } if( force || !oldText.equals( _check.getText() ) || _check.isEnabled() != wasEnabled ) { _cb.nodeChanged( _node ); } } void updateState() { String key = _table.get( _tableKey ); if( _check.isSelected() ) { if( key == null || !key.equals( _currentKeyFile ) ) { _table.put( _tableKey, _currentKeyFile ); _change = true; } } else { if( key != null && key.equals( _currentKeyFile ) ) { _table.remove( _tableKey ); _change = true; } } } } /** */ public void finalize() throws Throwable { ObjectCounter.remove( this ); super.finalize(); } public CodeSigningState() { ObjectCounter.add( this ); _textNoKey = Messages.PrivateKeyEditor_ProtectNoKey; _textPublic = Messages.PrivateKeyEditor_ProtectPublic; _textNonPublic = Messages.PrivateKeyEditor_ProtectNonPublic; } interface PackageParserCallback { public void setCurrentlyParsing( String name ); } public static class TreeNodeAndCheckBox { public TreeNodeAndCheckBox( TreeNode t, CheckBox c ) { tree = t; check = c; } TreeNode tree; CheckBox check; } /** * functions which are called back during dialog initialization */ public interface Callback extends PackageParserCallback { /** * callback to inform the UI which java file is being parsed */ public void setCurrentlyParsing( String name ); /** * callback to ask the UI to create a new tree node and check box element */ public TreeNodeAndCheckBox newTreeNodeAndCheckBox( TreeNode parent ); /** * callback to inform the UI that a node has changed significantly */ public void nodeChanged( TreeNode node ); } private Vector< OnePackage > parse( PackageParserCallback cb, Hashtable< String, String > packageProtection, Hashtable< String, String > classProtection ) throws CoreException { Hashtable< String, OnePackage > packageHash = new Hashtable< String, OnePackage >(); List< IFile > files; files = ProjectUtils.getProtectedFiles( _project ); for( IFile ifile : files ) { try { String name = ifile.getLocation().toOSString(); cb.setCurrentlyParsing( name ); File f = new File( name ); FileReader fr = new FileReader( f ); BufferedReader br = new BufferedReader( fr ); JavaParser parser = new JavaParser( f, br, false ); fr.close(); String packageName = parser.getPackage(); if( packageName != null && packageName.length() != 0 ) { OnePackage pack = packageHash.get( packageName ); if( pack == null ) { pack = new OnePackage( packageName ); packageHash.put( packageName, pack ); } for( int k = 0; k < parser.getClassCount(); ++k ) { pack.addClass( new OneClass( parser.getClassName( k ), parser.isClassPublic( k ) ) ); } pack.sortClasses(); } } catch( IOException ioe ) { _logger.error( ioe ); } } Vector< OnePackage > pakkages = Util.hashtableToVector( packageHash ); Util.stringSort( pakkages, true ); pakkages.insertElementAt( new OnePackage( NAME_NON_PUBLIC, _textNonPublic ), 0 ); pakkages.insertElementAt( new OnePackage( NAME_PUBLIC, _textPublic ), 0 ); return pakkages; } /** * Initialize the Package and class protector dialog data structures * * @param cb * An interface that is called back while parsing and building datastructures * @param node * The WorkspaceFile for the .key file * @throws CoreException */ public List< OnePackage > initialize( IProject project, BlackBerryProperties properties, IFile node, Callback cb ) throws CoreException { _project = project; _bbProperties = properties; _cb = cb; final Hashtable< String, String > packageProtection = _bbProperties._hiddenProperties.getPackageProtection(); final Hashtable< String, String > classProtection = _bbProperties._hiddenProperties.getClassProtection(); _change = false; List< OnePackage > packages = parse( cb, packageProtection, classProtection ); _allPackages = new Hashtable< String, String >(); _allClasses = new Hashtable< String, String >(); _tree = cb.newTreeNodeAndCheckBox( null ).tree; _temporaryPackageProtection = (Hashtable< String, String >) ( packageProtection.clone() ); _temporaryClassProtection = (Hashtable< String, String >) ( classProtection.clone() ); _allChecks = new Vector< ACheck >(); for( OnePackage pack : packages ) { String pakkage = pack.getName(); String displayName = pack.getDisplayName(); _allPackages.put( displayName, displayName ); TreeNodeAndCheckBox treeAndCheck = cb.newTreeNodeAndCheckBox( _tree ); pack.setCheck( treeAndCheck.check ); TreeNode packageNode = treeAndCheck.tree; _tree.addChild( packageNode ); ACheck parent = new ACheck( treeAndCheck.check, null, packageNode, _temporaryPackageProtection, pakkage, displayName ); _allChecks.add( parent ); for( int j = 0; j < pack.getNumClasses(); ++j ) { OneClass cls = pack.getClass( j ); String className = cls.getName(); String fullName = pakkage + "." + className; _allClasses.put( fullName, fullName ); treeAndCheck = cb.newTreeNodeAndCheckBox( packageNode ); CheckBox classCheck = treeAndCheck.check; cls.setCheck( classCheck ); TreeNode classNode = treeAndCheck.tree; packageNode.addChild( classNode ); _allChecks.add( new ACheck( classCheck, parent, classNode, _temporaryClassProtection, fullName, cls.toString() ) ); } } _allPackages.put( NAME_PUBLIC, NAME_PUBLIC ); _allPackages.put( NAME_NON_PUBLIC, NAME_NON_PUBLIC ); _keys = new Vector< String >(); _keyTitles = new Hashtable< String, String >(); _keys.add( _textNoKey ); _keyTitles.put( NAME_NO_KEY, Messages.PrivateKeyEditor_ProtectExplictlyUnsigned ); List< IFile > keyFiles = ProjectUtils.getKeyFiles( _project ); for( IFile f : keyFiles ) { String keyFile = f.getProjectRelativePath().toOSString(); if( f.getLocation().equals( node.getLocation() ) ) { _currentKeyFile = keyFile; } _keys.add( keyFile ); Properties keyContents = new Properties(); String keyName = ""; String keyId = ""; InputStream is = null; try { is = node.getContents(); keyContents.load( is ); keyName = keyContents.getProperty( "Name" ); keyId = keyContents.getProperty( "ID" ); } catch( IOException ioe ) { _logger.error( ioe ); } finally { if( is != null ) { try { is.close(); } catch( IOException e ) { } } } _keyTitles.put( keyFile, NLS.bind( Messages.PrivateKeyEditor_ProtectKeyTitle, new Object[] { keyFile, keyName, keyId } ) ); } Util.stringSort( _keys ); if( removeStaleEntries( _temporaryPackageProtection, _allPackages ) ) { _change = true; } if( removeStaleEntries( _temporaryClassProtection, _allClasses ) ) { _change = true; } updateChecksFromState( true ); _packageChecks = new ArrayList< CheckBox >(); _classChecks = new ArrayList< CheckBox >(); for( OnePackage pack : packages ) { _packageChecks.add( pack.getCheck() ); for( int j = 0; j < pack.getNumClasses(); ++j ) { OneClass cls = pack.getClass( j ); _classChecks.add( cls.getCheck() ); } } return packages; } private boolean removeStaleEntries( Hashtable< String, String > table, Hashtable< String, String > allValid ) { Enumeration< String > e = table.keys(); Vector< Object > toRemove = new Vector< Object >(); while( e.hasMoreElements() ) { Object key = e.nextElement(); if( allValid.get( key ) != null ) continue; toRemove.add( key ); } for( int i = toRemove.size() - 1; i >= 0; --i ) { table.remove( toRemove.elementAt( i ) ); } return toRemove.size() != 0; } /** * return the list of valid key file names ( for the dropdown combo box ) */ public Vector< String > getKeys() { Vector< String > v = new Vector< String >(); v.addAll( _keys ); return v; } private void updateChecksFromState( boolean force ) { for( int i = 0; i < _allChecks.size(); ++i ) { ( _allChecks.get( i ) ).updateCheck( force ); } } private void updateStateFromChecks() { for( int i = 0; i < _allChecks.size(); ++i ) { ( _allChecks.get( i ) ).updateState(); } } /** * used to inform this dialog when a checkbox has been updated */ public void updateCheck( CheckBox check ) { updateAllChecks( false ); } private void updateAllChecks( boolean force ) { updateStateFromChecks(); updateChecksFromState( force ); } /** * used to inform this dialog when the key file (dropdown) has been updated * * @param key * the name of the key file */ public void setKey( String key ) { _currentKeyFile = key; if( _currentKeyFile.equals( _textNoKey ) ) { _currentKeyFile = NAME_NO_KEY; } if( _currentKeyFile.length() != 0 ) { updateChecksFromState( false ); _tree.setText( _keyTitles.get( _currentKeyFile ) ); } updateAllChecks( true ); } /** * return the current key file * * @return the current key file */ public String getKey() { return _currentKeyFile; } /** * return the tree structure for the dialog */ public TreeNode getTree() { return _tree; } /** * return a list of packages and classes that are signed with the key * * @param node * WorkspaceFile .key file * @return list of packages and classes that are signed with the key * @throws CoreException */ public Vector< String > keyUsedBy( IFile node ) throws CoreException { _project = node.getProject(); Hashtable< String, String > packageProtection = _bbProperties._hiddenProperties.getPackageProtection(); Hashtable< String, String > classProtection = _bbProperties._hiddenProperties.getClassProtection(); PackageParserCallback cb = new PackageParserCallback() { public void setCurrentlyParsing( String name ) { } }; Vector< OnePackage > pakkages = parse( cb, packageProtection, classProtection ); if( pakkages == null ) { return new Vector< String >(); } String name = node.getProjectRelativePath().toOSString(); Vector< String > usingKey = new Vector< String >(); for( int i = 0; i < pakkages.size(); ++i ) { // Inspect packages OnePackage pack = pakkages.elementAt( i ); String pakkage = pack.getName(); String key = packageProtection.get( pakkage ); if( key != null && key.equals( name ) ) { usingKey.add( pakkage ); } for( int j = 0; j < pack.getNumClasses(); ++j ) { // classes OneClass cls = pack.getClass( j ); String className = cls.getName(); String fullName = pakkage + "." + className; key = classProtection.get( fullName ); if( key != null && key.equals( name ) ) { usingKey.add( fullName ); } } } return usingKey; } private void checkAll( List< CheckBox > v, boolean on ) { for( CheckBox check : v ) { if( check.isEnabled() ) { check.setEnabled( true ); check.setSelected( on ); } } } /** * check all of the enabled class check boxes */ public void checkEnabledClasses() { checkAll( _classChecks, true ); updateAllChecks( true ); } /** * check all of the enabled package check boxes */ public void checkEnabledPackages() { checkAll( _packageChecks, true ); updateAllChecks( true ); } /** * uncheck all of the enabled package and class check boxes */ public void clearEnabledPackagesAndClasses() { checkAll( _classChecks, false ); checkAll( _packageChecks, false ); updateAllChecks( true ); } /** * clear all checkboxes in the dialog, even the disabled ones */ public void clearAllPackagesAndClasses() { for( int i = 0; i < _allChecks.size(); ++i ) { ( _allChecks.get( i ) ).clear(); } updateAllChecks( true ); } /** * OK button clicked. Transfer the state from here to the underlying project */ public void ok() { if( _change ) { _bbProperties._hiddenProperties.setPackageProtection( _temporaryPackageProtection ); _bbProperties._hiddenProperties.setClassProtection( _temporaryClassProtection ); } } }