From 87665ebc18753379bbee11a30888f53b093909ed Mon Sep 17 00:00:00 2001 From: Jihoon Song Date: Wed, 14 Nov 2012 21:28:28 +0900 Subject: [PATCH] [Title] common-eplugin: applied Prompter logic using DynamicDialog [Desc.] [Issue] Change-Id: I35c97b4f44707330d718c43bded1f07dce988cb7 --- .../command/ReadSigningProfileFileCommand.java | 249 +++++++++++++++------ .../tizen/common/sign/util/SigningProfileUtil.java | 84 +++++++ .../src/org/tizen/common/sign/util/XMLUtil.java | 59 +++++ .../common/core/command/PrompterConstants.java | 5 +- .../core/command/prompter/EclipsePrompter.java | 3 +- .../common/core/command/prompter/SWTPrompter.java | 32 ++- .../org/tizen/common/file/EclipseFileHandler.java | 7 + .../org/tizen/common/ui/dialog/DynamicDialog.java | 2 +- 8 files changed, 374 insertions(+), 67 deletions(-) diff --git a/org.tizen.common.sign/src/org/tizen/common/sign/command/ReadSigningProfileFileCommand.java b/org.tizen.common.sign/src/org/tizen/common/sign/command/ReadSigningProfileFileCommand.java index 5df5735..969e68f 100644 --- a/org.tizen.common.sign/src/org/tizen/common/sign/command/ReadSigningProfileFileCommand.java +++ b/org.tizen.common.sign/src/org/tizen/common/sign/command/ReadSigningProfileFileCommand.java @@ -26,88 +26,211 @@ package org.tizen.common.sign.command; import static org.tizen.common.util.IOUtil.tryClose; +import java.io.ByteArrayInputStream; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tizen.common.core.command.AbstractCommand; import org.tizen.common.core.command.ExecutionContext; import org.tizen.common.core.command.Executor; +import org.tizen.common.core.command.UserField; import org.tizen.common.file.FileHandler; import org.tizen.common.file.FileHandler.Attribute; import org.tizen.common.file.FileHandler.Type; +import org.tizen.common.sign.model.SignatureConstants; import org.tizen.common.sign.preferences.SigningProfileItem; import org.tizen.common.sign.util.SigningProfileUtil; +import org.tizen.common.util.IOUtil; public class ReadSigningProfileFileCommand extends AbstractCommand { - protected final Logger logger = LoggerFactory.getLogger( getClass() ); - - protected final String path; - - protected final String profileName; - - protected List profileItems = new ArrayList(); - - public ReadSigningProfileFileCommand( final String path, final String profileName ) - { - this.path = path; - this.profileName = profileName; - - logger.info( "Read profile[{}] from {}", profileName, path ); - } - - public void run( - final Executor executor, - final ExecutionContext context - ) - throws Exception - { - final FileHandler fileHandler = context.getFileHandler(); - if ( !canRun( context ) ) - { - return ; - } - - final InputStream in = fileHandler.read( path ); - - try - { - profileItems = SigningProfileUtil.parseProfileFile( in, profileName ); - } - finally - { - tryClose( in ); - } - } - - protected - boolean - canRun( - final ExecutionContext context - ) - throws IOException - { - final FileHandler fileHandler = context.getFileHandler(); - if ( !fileHandler.is( path, Attribute.EXISTS ) ) - { - return false; - } - if ( !fileHandler.is( path, Attribute.READABLE ) ) - { - return false; - } - return ( Type.FILE.equals( fileHandler.get( path, Attribute.TYPE ) ) ); - } - - public List getProfileItems() - { - return this.profileItems; - } + protected final Logger logger = LoggerFactory.getLogger( getClass() ); + protected final String path; + + protected final String profileName; + + protected InputStream profileInputStream; + + protected List profileItems = new ArrayList(); + + + public ReadSigningProfileFileCommand( final String path, final String profileName ) + { + this.path = path; + this.profileName = profileName; + + logger.info( "Read profile[{}] from {}", profileName, path ); + } + + public void run( + final Executor executor, + final ExecutionContext context + ) + throws Exception + { + if ( !canRun( context ) ) + { + return ; + } + + final FileHandler fileHandler = context.getFileHandler(); + byte[] bytes = IOUtil.getBytes( fileHandler.read( this.path ) , true); + this.profileInputStream = new ByteArrayInputStream( bytes ); + + try { + this.profileItems = SigningProfileUtil.parseProfileFile( this.profileInputStream, this.profileName ); + + interactForPassword( executor, context ); + } finally { + tryClose( this.profileInputStream ); + this.profileInputStream = null; + } + } + + protected void interactForPassword(final Executor executor, final ExecutionContext context) + throws Exception { + SigningProfileItem authorItem = getItem( true ); + SigningProfileItem distributorItem = getItem( false ); + + boolean hasAuthor = isValidItem( authorItem ); + boolean hasDistributor = isValidItem( distributorItem ); + + if ( !hasAuthor ) { + // If don't have author certificate, no interact. + return; + } + + if ( !SigningProfileUtil.hasPassword( authorItem ) || + ( hasDistributor && !SigningProfileUtil.hasPassword( distributorItem ) ) ) { + + ArrayList fieldList = new ArrayList(); + + // Author password text + UserField authorText = new UserField( SignatureConstants.AUTHOR, "Author password : ", char[].class ); + authorText.setValue( authorItem.getPassword() ); + fieldList.add( authorText ); + + // Savable author password button + UserField authorButton = new UserField( "authorPwdSavable", "Save author password", boolean.class ); + fieldList.add( authorButton ); + + // Distributor password text + UserField distributor2Text = new UserField( SignatureConstants.DISTRIBUTOR_2ND, "Distributor password : ", char[].class ); + if ( !hasDistributor ) { + distributor2Text.setModify( false ); + } else { + distributor2Text.setValue( distributorItem.getPassword() ); + } + fieldList.add( distributor2Text ); + + // Savable distributor2 password button + UserField distributor2Button = new UserField( "distributorPwdSavable", "Save author password", boolean.class ); + if ( !hasDistributor ) { + distributor2Button.setModify( false ); + } + fieldList.add( distributor2Button ); + + // options + Map options = new HashMap(); + + + // Interact passwords + context.getPrompter().batch( fieldList, options ); + + + // save profiles + boolean saveAuthor = getBoolean( authorButton.getValue() ); + boolean saveDistributor = hasDistributor ? saveDistributor = getBoolean( distributor2Button.getValue() ) : false; + + if ( saveAuthor ) { + authorItem.setPassword( (char[]) authorText.getValue() ); + } + if ( hasDistributor && saveDistributor ) { + distributorItem.setPassword( (char[]) distributor2Text.getValue() ); + } + + writeProfiles( executor, context ); + + // synchronize profile password + if ( !saveAuthor ) { + authorItem.setPassword( (char[]) authorText.getValue() ); + } + if ( hasDistributor && !saveDistributor ) { + distributorItem.setPassword( (char[]) distributor2Text.getValue() ); + } + } + } + + protected void writeProfiles(final Executor executor, final ExecutionContext context) + throws Exception { + final FileHandler fileHandler = context.getFileHandler(); + + this.profileInputStream.reset(); + final InputStream in = SigningProfileUtil.updateProfiles( this.profileInputStream, this.profileName, this.profileItems ); + if ( in == null ) { + logger.warn( "Cannot update profiles" ); + return; + } + + try { + fileHandler.write( this.path, in ); + } finally { + tryClose( in ); + } + } + + protected + boolean + canRun( + final ExecutionContext context + ) + throws IOException + { + final FileHandler fileHandler = context.getFileHandler(); + if ( !fileHandler.is( path, Attribute.EXISTS ) ) + { + return false; + } + if ( !fileHandler.is( path, Attribute.READABLE ) ) + { + return false; + } + return ( Type.FILE.equals( fileHandler.get( path, Attribute.TYPE ) ) ); + } + + public List getProfileItems() + { + return this.profileItems; + } + + protected boolean isValidItem(SigningProfileItem item) { + if ( item == null ) { + return false; + } + + return SigningProfileUtil.hasKeyLocation( item ); + } + + protected SigningProfileItem getItem(boolean author) { + for ( SigningProfileItem item : this.profileItems ) { + if ( item.isAuthor() == author ) { + return item; + } + } + + return null; + } + + protected boolean getBoolean(Object obj) { + return ( obj == null ) ? true : Boolean.parseBoolean( String.valueOf( obj ) ); + } } diff --git a/org.tizen.common.sign/src/org/tizen/common/sign/util/SigningProfileUtil.java b/org.tizen.common.sign/src/org/tizen/common/sign/util/SigningProfileUtil.java index 73f3fc0..8f11745 100644 --- a/org.tizen.common.sign/src/org/tizen/common/sign/util/SigningProfileUtil.java +++ b/org.tizen.common.sign/src/org/tizen/common/sign/util/SigningProfileUtil.java @@ -26,6 +26,8 @@ package org.tizen.common.sign.util; import static org.tizen.common.util.StringUtil.isEmpty; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -58,6 +60,8 @@ import org.tizen.common.sign.Activator; import org.tizen.common.sign.preferences.SigningPreferencePage; import org.tizen.common.sign.preferences.SigningProfile; import org.tizen.common.sign.preferences.SigningProfileItem; +import org.tizen.common.util.Assert; +import org.tizen.common.util.IOUtil; import org.tizen.common.util.ObjectUtil; import org.tizen.common.util.ParsingUtil; import org.tizen.common.util.StringUtil; @@ -477,4 +481,84 @@ public class SigningProfileUtil } return null; } + + public static boolean hasKeyLocation( final SigningProfileItem item ) { + Assert.notNull( item ); + + final String keyLocation = item.getKeyLocation(); + + return ( keyLocation == null ) ? false : !keyLocation.trim().isEmpty(); + } + + /** + * Whether a profile item has password or not + * @param item {@link SigningProfileItem} + * @return If has password, return true. otherwise, return false. + */ + public static boolean hasPassword( final SigningProfileItem item ) { + Assert.notNull( item ); + + final char[] password = item.getPassword(); + + return ( password == null ) ? false : !( new String( password ).trim().isEmpty() ); + } + + /** + * Getter for element by profile name + * @param doc Document + * @param profileName profile name + * @return profile Element + */ + protected static Element getProfileElement(Document doc, String profileName) { + Assert.notNull( doc ); + Assert.notNull( profileName ); + + NodeList profileNodeList = doc.getElementsByTagName( ELEMENT_PROFILE ); + for ( int i = 0; i < profileNodeList.getLength(); i++ ) { + Node profileNode = profileNodeList.item( i ); + boolean matched = XMLUtil.isElementByAttributeWithValue( profileNode, PROFILE_ATTR_NAME, profileName ); + if ( matched ) { + return (Element) profileNode; + } + } + + return null; + } + + public static InputStream updateProfiles( InputStream in, final String profileName, final List items ) + throws ParserConfigurationException, SAXException, IOException, TransformerException { + Assert.notNull( in ); + Assert.notNull( profileName ); + Assert.notNull( items ); + + // read current model + Document doc = XMLUtil.create( in ); + + // find profile + Element profileElement = getProfileElement( doc, profileName ); + if ( profileElement == null ) { + // not found profile element. cannot update profile + return null; + } + + // remove old profile items + XMLUtil.removeChildElements( profileElement ); + + // add new profile items + for ( SigningProfileItem item : items ) { + Element itemElement = createSigningProfileItem( doc, item, item.isAuthor() ? "true" : "false" ); + profileElement.appendChild( itemElement ); + } + + // get stream + // TODO: will improve to effective solution. + ByteArrayOutputStream outputStream = null; + try { + outputStream = XMLUtil.createOutputStreamFromDocument( doc ); + } finally { + IOUtil.tryClose( outputStream ); + } + + return new ByteArrayInputStream( outputStream.toByteArray() ); + } } diff --git a/org.tizen.common.sign/src/org/tizen/common/sign/util/XMLUtil.java b/org.tizen.common.sign/src/org/tizen/common/sign/util/XMLUtil.java index 9513d2e..1b61bab 100644 --- a/org.tizen.common.sign/src/org/tizen/common/sign/util/XMLUtil.java +++ b/org.tizen.common.sign/src/org/tizen/common/sign/util/XMLUtil.java @@ -24,14 +24,23 @@ */ package org.tizen.common.sign.util; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.tizen.common.util.Assert; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.xml.sax.SAXException; /** @@ -93,4 +102,54 @@ public class XMLUtil return builder.newDocument(); } + /** + * All of the child nodes are removed. + * @param element + */ + public static void removeChildElements(Element element) { + Assert.notNull( element ); + + while ( element.hasChildNodes() ) { + element.removeChild( element.getFirstChild() ); + } + } + + /** + * Whether element has attribute value with name + * @param node Element instance + * @param attributeName attribute name + * @param attributeValue attribute value + * @return + */ + public static boolean isElementByAttributeWithValue(Node node, String attributeName, String attributeValue) { + Assert.notNull( node ); + Assert.notNull( attributeName ); + Assert.notNull( attributeValue ); + + if ( node instanceof Element ) { + String attribute = ( (Element) node ).getAttribute( attributeName ); + return attributeValue.equals( attribute ); + } + + return false; + } + + /** + * Write stream from Document. and return this. + * @param doc Document + * @return a writed ByteArrayOutputStream + * @throws TransformerException + */ + public static ByteArrayOutputStream createOutputStreamFromDocument(Document doc) throws TransformerException { + Assert.notNull( doc ); + + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + + transformer.transform( new DOMSource( doc ), new StreamResult( outStream ) ); + + return outStream; + } } diff --git a/org.tizen.common/src/org/tizen/common/core/command/PrompterConstants.java b/org.tizen.common/src/org/tizen/common/core/command/PrompterConstants.java index 143fbb2..f485af7 100755 --- a/org.tizen.common/src/org/tizen/common/core/command/PrompterConstants.java +++ b/org.tizen.common/src/org/tizen/common/core/command/PrompterConstants.java @@ -29,5 +29,8 @@ public class PrompterConstants public static final String SUPPORT_CONSOLE = "console"; public static final String SUPPORT_IDE = "eclipse"; - + + public static final String OPTION_TITLE = "title"; + + public static final String OPTION_DIALOG_RETURN_CODE = "returnCode"; } diff --git a/org.tizen.common/src/org/tizen/common/core/command/prompter/EclipsePrompter.java b/org.tizen.common/src/org/tizen/common/core/command/prompter/EclipsePrompter.java index fc24a39..0427b3d 100755 --- a/org.tizen.common/src/org/tizen/common/core/command/prompter/EclipsePrompter.java +++ b/org.tizen.common/src/org/tizen/common/core/command/prompter/EclipsePrompter.java @@ -212,7 +212,8 @@ implements Prompter final Map options ) { - // TODO implement + final Prompter prompter = getPrompter(); + prompter.batch( userFields, options ); } } diff --git a/org.tizen.common/src/org/tizen/common/core/command/prompter/SWTPrompter.java b/org.tizen.common/src/org/tizen/common/core/command/prompter/SWTPrompter.java index 3f53b1c..c11348b 100755 --- a/org.tizen.common/src/org/tizen/common/core/command/prompter/SWTPrompter.java +++ b/org.tizen.common/src/org/tizen/common/core/command/prompter/SWTPrompter.java @@ -35,12 +35,17 @@ import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.window.Window; import org.tizen.common.core.command.Prompter; +import org.tizen.common.core.command.PrompterConstants; import org.tizen.common.core.command.UserField; +import org.tizen.common.ui.dialog.DynamicDialog; import org.tizen.common.ui.dialog.PasswordInputDialog; import org.tizen.common.util.NotificationType; import org.tizen.common.util.NotifierDialog; @@ -244,7 +249,32 @@ implements Prompter final Map options ) { - // TODO implement + final AtomicInteger returnCode = new AtomicInteger(); + + syncExec( new Runnable() { + @Override + public void run() { + DynamicDialog dynamicDialog = new DynamicDialog( userFields ); + + // set title from options + if ( options != null && options.containsKey( PrompterConstants.OPTION_TITLE ) ) { + Object object = options.get( PrompterConstants.OPTION_TITLE ); + if ( object instanceof String ) { + dynamicDialog.setTitle( (String) object ); + } + } + + returnCode.set( dynamicDialog.open() ); + + if (options != null) { + options.put( PrompterConstants.OPTION_DIALOG_RETURN_CODE, new Integer( returnCode.get() ) ); + } + } + } ); + + if ( returnCode.get() == Window.CANCEL ) { + throw new OperationCanceledException("Cancel interaction."); + } } } diff --git a/org.tizen.common/src/org/tizen/common/file/EclipseFileHandler.java b/org.tizen.common/src/org/tizen/common/file/EclipseFileHandler.java index 9b032f9..0b2ca71 100755 --- a/org.tizen.common/src/org/tizen/common/file/EclipseFileHandler.java +++ b/org.tizen.common/src/org/tizen/common/file/EclipseFileHandler.java @@ -316,6 +316,13 @@ extends StandardFileHandler public void write(String path, InputStream out) throws IOException { logger.trace( "Path :{}", path ); final IResource file = getResource( path ); + + // Bugfix : In case of location without workspace, NPE occur. + if ( file == null ) { + super.write( path, out ); + return; + } + final IFile f = (IFile) file.getAdapter( IFile.class ); try { diff --git a/org.tizen.common/src/org/tizen/common/ui/dialog/DynamicDialog.java b/org.tizen.common/src/org/tizen/common/ui/dialog/DynamicDialog.java index 97474b2..af4c35b 100644 --- a/org.tizen.common/src/org/tizen/common/ui/dialog/DynamicDialog.java +++ b/org.tizen.common/src/org/tizen/common/ui/dialog/DynamicDialog.java @@ -238,7 +238,7 @@ public class DynamicDialog extends Dialog { * @return Text's default style */ public int getTextStyle() { - return SWT.SINGLE; + return SWT.SINGLE | SWT.BORDER; } -- 2.7.4