Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/GrantEntry.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/GrantEntry.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/GrantEntry.java (working copy) @@ -18,8 +18,11 @@ package org.apache.harmony.tools.policytool.model; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import org.apache.harmony.tools.policytool.Consts; + /** * Represents an entry which specifies some grant permission. */ @@ -25,8 +28,10 @@ */ public class GrantEntry extends PolicyEntry { - /** Keyword of the keystore entry in the policy text. */ - public static final String KEYWORD = "grant"; + /** Keyword of the keystore entry in the policy text. */ + public static final String KEYWORD = "grant"; + /** Stored value of the lowercased keyword for fast policy parsing. */ + public static final String LOWERED_KEYWORD = KEYWORD.toLowerCase(); /** Code base of the grant entry. */ private String codeBase; @@ -104,13 +109,42 @@ @Override public String getText() { - // TODO Auto-generated method stub - return null; - } + final StringBuilder textBuilder = new StringBuilder( KEYWORD ); + + if ( signedBy != null && signedBy.length() > 0 ) + textBuilder.append( " signedBy \"" ).append( signedBy ).append( '"' ); + + if ( codeBase != null && codeBase.length() > 0 ) { + if ( signedBy != null && signedBy.length() > 0 ) + textBuilder.append( ',' ); + textBuilder.append( " codeBase \"" ).append( codeBase ).append( '"' ); + } - @Override - public void setText( final String entryText ) { - // TODO Auto-generated method stub + if ( principalList.isEmpty() ) + textBuilder.append( ' ' ); + else { + if ( signedBy != null && signedBy.length() > 0 + || codeBase != null && codeBase.length() > 0 ) + textBuilder.append( ',' ); + textBuilder.append( Consts.NEW_LINE_STRING ); + + for ( final Iterator< Principal > principalIterator = principalList.iterator(); principalIterator.hasNext(); ) { + textBuilder.append( " " ).append( principalIterator.next().toString() ); + if ( principalIterator.hasNext() ) + textBuilder.append( ',' ).append( Consts.NEW_LINE_STRING ); + else + textBuilder.append( ' ' ); + } + } + + textBuilder.append( '{' ).append( Consts.NEW_LINE_STRING ); + + for ( final Permission permission : permissionList ) + textBuilder.append( "\tpermission " ).append( permission.toString( Consts.NEW_LINE_STRING + "\t\t" ) ).append( TERMINATOR_CHAR ).append( Consts.NEW_LINE_STRING ); + + textBuilder.append( '}' ).append( TERMINATOR_CHAR ); + + return textBuilder.toString(); } @Override Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/PolicyEntry.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/PolicyEntry.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/PolicyEntry.java (working copy) @@ -26,7 +26,8 @@ public static final char TERMINATOR_CHAR = ';'; /** - * Returns the policy entry text. + * Returns the policy entry text.
+ * Should not contain a line separator in the end but TERMINATOR_CHAR must be included. * @return the policy entry text */ public abstract String getText(); @@ -31,10 +32,4 @@ */ public abstract String getText(); - /** - * Sets the policy entry text. - * @param entryText policy entry text to be set - */ - public abstract void setText( final String entryText ); - } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Permission.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Permission.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Permission.java (working copy) @@ -96,6 +96,29 @@ } @Override + public String toString() { + return toString( "" ); + } + + /** + * Returns a string representation of the permission. + * @param signedByPartPrefix string to be put before the signedBy part; omitted if signedBy is missing + * @return a string representation of the permission + */ + public String toString( final String signedByPartPrefix ) { + final StringBuilder stringBuilder = new StringBuilder( className ); + + if ( targetName != null && targetName.length() > 0 ) + stringBuilder.append( " \"" ).append( targetName ).append( '"' );; + if ( actions != null && actions.length() > 0 ) + stringBuilder.append( ", \"" ).append( actions ).append( '"'); + if ( signedBy != null && signedBy.length() > 0 ) + stringBuilder.append( "," ).append( signedByPartPrefix ).append( "signedBy \"" ).append( signedBy ).append( '"' ); + + return stringBuilder.toString(); + } + + @Override public Object clone() { try { return super.clone(); @@ -100,7 +123,7 @@ try { return super.clone(); } catch ( final CloneNotSupportedException cnse ) { - // This never gonna happen. + // This' never gonna happen. cnse.printStackTrace(); return null; } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Principal.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Principal.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/Principal.java (working copy) @@ -61,7 +61,7 @@ @Override public String toString() { - return "Principal " + type + " \"" + name + '"'; + return "principal " + type + " \"" + name + '"'; } @Override @@ -69,7 +69,7 @@ try { return super.clone(); } catch ( final CloneNotSupportedException cnse ) { - // This never gonna happen. + // This' never gonna happen. cnse.printStackTrace(); return null; } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystoreEntry.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystoreEntry.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystoreEntry.java (working copy) @@ -29,8 +29,10 @@ */ public class KeystoreEntry extends PolicyEntry { - /** Keyword of the keystore entry in the policy text. */ - public static final String KEYWORD = "keystore"; + /** Keyword of the keystore entry in the policy text. */ + public static final String KEYWORD = "keystore"; + /** Stored value of the lowercased keyword for fast policy parsing. */ + public static final String LOWERED_KEYWORD = KEYWORD.toLowerCase(); /** URL of the keystore. */ private String url; @@ -90,27 +92,16 @@ @Override public String getText() { final StringBuilder textBuilder = new StringBuilder( KEYWORD ); - boolean firstParamAdded = false; - if ( url != null ) { + if ( url != null && url.length() > 0 ) { textBuilder.append( " \"" ).append( url ).append( '"' ); - firstParamAdded = true; - } - if ( type != null ) { - if ( firstParamAdded ) - textBuilder.append( ',' ); - else - firstParamAdded = true; - textBuilder.append( " \"" ).append( type ).append( '"' ); - } + if ( type != null && type.length() > 0 ) { + textBuilder.append( ", \"" ).append( type ).append( '"' ); - if ( provider != null ) { - if ( firstParamAdded ) - textBuilder.append( ',' ); - else - firstParamAdded = true; - textBuilder.append( " \"" ).append( provider ).append( '"' ); + if ( provider != null && provider.length() > 0 ) + textBuilder.append( ", \"" ).append( provider ).append( '"' ); + } } textBuilder.append( TERMINATOR_CHAR ); @@ -118,9 +109,4 @@ return textBuilder.toString(); } - @Override - public void setText( final String entryText ) { - // TODO Auto-generated method stub - } - } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystorePasswordURLEntry.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystorePasswordURLEntry.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/KeystorePasswordURLEntry.java (working copy) @@ -27,8 +27,10 @@ */ public class KeystorePasswordURLEntry extends PolicyEntry { - /** Keyword of the keystore password URL entry in the policy text. */ - public static final String KEYWORD = "keystorePasswordURL"; + /** Keyword of the keystore password URL entry in the policy text. */ + public static final String KEYWORD = "keystorePasswordURL"; + /** Stored value of the lowercased keyword for fast policy parsing. */ + public static final String LOWERED_KEYWORD = KEYWORD.toLowerCase(); /** URL of the keystore password. */ private String url; @@ -54,9 +56,4 @@ return KEYWORD + " \"" + url + '\"' + TERMINATOR_CHAR; } - @Override - public void setText( final String entryText ) { - // TODO Auto-generated method stub - } - } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/CommentEntry.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/CommentEntry.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/model/CommentEntry.java (working copy) @@ -43,7 +43,10 @@ return entryText; } - @Override + /** + * Sets the text of the comment entry. + * @param entryText + */ public void setText( final String entryText ) { this.entryText = entryText; } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GraphicalEditorPanel.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GraphicalEditorPanel.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GraphicalEditorPanel.java (working copy) @@ -21,6 +21,11 @@ import java.util.ArrayList; import java.util.List; +import javax.swing.JOptionPane; + +import org.apache.harmony.tools.policytool.Consts; +import org.apache.harmony.tools.policytool.control.InvalidPolicyTextException; +import org.apache.harmony.tools.policytool.control.PolicyTextParser; import org.apache.harmony.tools.policytool.model.GrantEntry; import org.apache.harmony.tools.policytool.model.KeystoreEntry; import org.apache.harmony.tools.policytool.model.KeystorePasswordURLEntry; @@ -35,6 +40,7 @@ private String invalidPolicyText; /** The list of the policy text's entries or null if invalid policy text was loaded. */ + // TODO: if concurrent modification exception occurs, then make this list synchronized! private List< PolicyEntry > policyEntryList = new ArrayList< PolicyEntry >(); /** ListAndEditPanel for handling the grant entries. */ @@ -39,6 +45,7 @@ /** ListAndEditPanel for handling the grant entries. */ private ListAndEditPanel< PolicyEntry > grantEntryLAEPanel; + /** * Creates a new GraphicalEditorPanel.
* Sets a BorderLayout as the layout manager. @@ -45,7 +52,7 @@ * @param mainFrame reference to the main frame */ public GraphicalEditorPanel( final MainFrame mainFrame ) { - super( mainFrame, "Graphical editing", new BorderLayout(), true ); + super( mainFrame, "Graphical editing", new BorderLayout() ); buildGUI(); } @@ -58,6 +65,9 @@ if ( grantEntryLAEPanel != null ) remove( grantEntryLAEPanel ); + if ( invalidPolicyText != null ) + return; + grantEntryLAEPanel = new ListAndEditPanel< PolicyEntry >( "Policy entries:", "Policy Entry", policyEntryList, new ListAndEditPanel.Filter< PolicyEntry > () { public boolean includeEntity( final PolicyEntry entity ) { @@ -76,12 +86,19 @@ @Override public boolean loadPolicyText( final String policyText ) { - this.invalidPolicyText = policyText; - policyEntryList = new ArrayList< PolicyEntry >(); + try { + invalidPolicyText = null; - //TODO: uncomment when loadPolicyText() is implemented - //buildGUI(); + policyEntryList = PolicyTextParser.parsePolicyText( policyText ); + + } catch ( final InvalidPolicyTextException ipte ) { + JOptionPane.showMessageDialog( this, new String[] { ipte.getMessage(), " ", "Graphical editor is disabled, correct the error in the direct editor or load a valid policy file." }, "Parse error!", JOptionPane.ERROR_MESSAGE ); + invalidPolicyText = policyText; + policyEntryList = new ArrayList< PolicyEntry >(); + } + + buildGUI(); return true; } @@ -88,7 +105,16 @@ @Override public String getPolicyText() { - return invalidPolicyText; + if ( invalidPolicyText != null ) + return invalidPolicyText; + + final StringBuilder policyTextBuilder = new StringBuilder(); + + for ( final PolicyEntry policyEntry : policyEntryList ) { + policyTextBuilder.append( policyEntry.getText() ).append( Consts.NEW_LINE_STRING ); + } + + return policyTextBuilder.toString(); } /** @@ -114,4 +140,13 @@ new KeystoreEntryEditFormDialog( mainFrame, this, keystoreEntry, keystorePasswordURLEntry, policyEntryList ).setVisible( true ); } + /** + * We don't want to support graphical keystore edit if the loaded policy text is invalid. + * @return true if the loaded policy text is valid; false otherwise + */ + @Override + public boolean supportsGraphicalKeystoreEdit() { + return invalidPolicyText == null; + } + } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PrincipalEditFormDialog.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PrincipalEditFormDialog.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PrincipalEditFormDialog.java (working copy) @@ -109,6 +109,7 @@ @Override public void onOkButtonPressed() { // TODO: validation + // Class name and target name are mandatory! final Principal principal = initialPrincipal == null ? new Principal() : initialPrincipal; @@ -118,8 +119,7 @@ if ( initialPrincipal == null ) { principalList.add( principal ); listModel.addElement( principal ); - } else - refreshVisualizationList(); + } finishSuccessfulEdit( false ); } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GrantEntryEditFormDialog.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GrantEntryEditFormDialog.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/GrantEntryEditFormDialog.java (working copy) @@ -185,8 +185,7 @@ if ( initialGrantEntry == null ) { policyEntryList.add( grantEntry ); listModel.addElement( grantEntry ); - } else - refreshVisualizationList(); + } finishSuccessfulEdit(); } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/ListAndEditPanel.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/ListAndEditPanel.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/ListAndEditPanel.java (working copy) @@ -18,6 +18,7 @@ package org.apache.harmony.tools.policytool.view; import java.awt.BorderLayout; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; @@ -36,6 +37,9 @@ */ public class ListAndEditPanel< EntityType > extends JPanel implements ActionListener { + /** Preferred dimension of the component listing the entities. */ + private static final Dimension PREFERRED_LIST_COMPONENT_SIZE = new Dimension( 310, 150 ); + /** Model of the list component displaying the entities. */ private final DefaultListModel listModel = new DefaultListModel(); /** The component to list he entities. */ @@ -140,7 +144,9 @@ add( buttonsPanel, BorderLayout.NORTH ); - add( new JScrollPane( entityListComponent ), BorderLayout.CENTER ); + final JScrollPane scrollPane = new JScrollPane( entityListComponent ); + scrollPane.setPreferredSize( PREFERRED_LIST_COMPONENT_SIZE ); + add( scrollPane, BorderLayout.CENTER ); setBorder( BorderFactory.createTitledBorder( panelTitle ) ); } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/EditorPanel.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/EditorPanel.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/EditorPanel.java (working copy) @@ -39,6 +39,18 @@ /** * Creates a new EditorPanel.
+ * Calls the other constructor with an additional supportsGraphicalKeystoreEdit=false. + * @param mainFrame reference to the main frame + * @param panelTitle the title of the panel + * @param layoutManager layout manager to be used + * @param supportsGraphicalKeystoreEdit true if this editor panel supports graphical keystore edit; false otherwise + */ + public EditorPanel( final MainFrame mainFrame, final String panelTitle, final LayoutManager layoutManager ) { + this( mainFrame, panelTitle, layoutManager, false ); + } + + /** + * Creates a new EditorPanel.
* Awaits a layout manager to be sent to the super class. * @param mainFrame reference to the main frame * @param panelTitle the title of the panel Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/KeystoreEntryEditFormDialog.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/KeystoreEntryEditFormDialog.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/KeystoreEntryEditFormDialog.java (working copy) @@ -94,7 +94,7 @@ } if ( initialKeystorePasswordURLEntry != null ) { - keystorePasswordURLTextField.setText( initialKeystorePasswordURLEntry.getUrl() ); + keystorePasswordURLTextField.setText( initialKeystorePasswordURLEntry.getUrl() ); } final JPanel flowPanel = new JPanel(); Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PermissionEditFormDialog.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PermissionEditFormDialog.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/PermissionEditFormDialog.java (working copy) @@ -29,6 +29,7 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; @@ -186,6 +187,40 @@ panel.add( new JLabel( "Signed By:" ) ); panel.add( signedByTextField ); + if ( initialPermission != null ) { + // Should we choose anything in the permission type combo box? + if ( initialPermission.getClassName() != null ) + for ( int i = 0; i < DEFAULT_PERMISSION_TYPE_CLASS_NAMES.length; i++ ) + if ( DEFAULT_PERMISSION_TYPE_CLASS_NAMES[ i ] != null && DEFAULT_PERMISSION_TYPE_CLASS_NAMES[ i ].equals( initialPermission.getClassName() ) ) { + permissionTypeComboBox.setSelectedIndex( i ); + break; + } + permissionTypeTextField.setText( initialPermission.getClassName() ); + + final String[][] targetNameActions = permissionTypeTargetNamesActionsMap.get( DEFAULT_PERMISSION_TYPE_NAMES[ permissionTypeComboBox.getSelectedIndex() ] ); + final String[] targetNames = targetNameActions[ 0 ]; + final String[] actions = targetNameActions[ 1 ]; + + // Should we choose anything in the target name combo box? + if ( initialPermission.getTargetName() != null && targetNames != null ) + for ( int i = 0; i < targetNames.length; i++ ) + if ( targetNames[ i ].equals( initialPermission.getTargetName() ) ) { + targetNameComboBox.setSelectedIndex( i+1 ); // +1 for the constant "Target Name:" item + break; + } + targetNameTextField.setText( initialPermission.getTargetName() ); + + // Should we choose anything in the actions combo box? + if ( initialPermission.getActions() != null && actions != null ) + for ( int i = 0; i < actions.length; i++ ) + if ( actions[ i ].equals( initialPermission.getActions() ) ) { + actionsComboBox.setSelectedIndex( i ); // +1 for the constant "Actions:" item + break; + } + actionsTextField.setText( initialPermission.getActions() ); + + signedByTextField.setText( initialPermission.getSignedBy() ); + } final JPanel flowPanel = new JPanel(); @@ -196,8 +231,24 @@ @Override public void onOkButtonPressed() { // TODO: validation - // TODO Auto-generated method stub + if ( permissionTypeTextField.getText().length() == 0 || targetNameTextField.isEnabled() && targetNameTextField.getText().length() == 0 ) { + JOptionPane.showMessageDialog( this, "Permission and target name must have a value!", "Error", JOptionPane.ERROR_MESSAGE ); + return; + } + final Permission permission = initialPermission == null ? new Permission() : initialPermission; + + permission.setClassName ( permissionTypeTextField.getText() ); + permission.setTargetName( targetNameTextField .getText() ); + permission.setActions ( actionsTextField .getText() ); + permission.setSignedBy ( signedByTextField .getText() ); + + if ( initialPermission == null ) { + permissionList.add( permission ); + listModel.addElement( permission ); + } + + finishSuccessfulEdit( false ); } } Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/DirectTextEditorPanel.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/DirectTextEditorPanel.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/DirectTextEditorPanel.java (working copy) @@ -18,7 +18,6 @@ package org.apache.harmony.tools.policytool.view; import java.awt.BorderLayout; -import java.awt.Font; import javax.swing.JScrollPane; import javax.swing.JTextArea; @@ -41,9 +40,10 @@ * @param mainFrame reference to the main frame */ public DirectTextEditorPanel( final MainFrame mainFrame ) { - super( mainFrame, "Direct editing", new BorderLayout(), false ); + super( mainFrame, "Direct editing", new BorderLayout() ); - policyTextTextArea.setFont( new Font( "Courier New", Font.PLAIN, Consts.DIRECT_EDITING_FONT_SIZE ) ); + policyTextTextArea.setFont( Consts.DIRECT_EDITING_FONT ); + policyTextTextArea.setTabSize( Consts.DIRECT_EDITING_TAB_SIZE ); // We want to track changes of the document so we can ask confirmation on exit policyTextTextArea.getDocument().addDocumentListener( new DocumentListener() { Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/LAEFormDialog.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/LAEFormDialog.java (revision 683377) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/view/LAEFormDialog.java (working copy) @@ -64,6 +64,12 @@ this.listModel = listModel; } + @Override + protected void finishSuccessfulEdit( final boolean setDirtyFlag ) { + refreshVisualizationList(); + super.finishSuccessfulEdit( setDirtyFlag ); + } + /** * Refreshes the visualization list.
* Should be called if the entities of the list might have changed but the list model was not modified. Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/PolicyTextParser.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/PolicyTextParser.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/PolicyTextParser.java (revision 0) @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.tools.policytool.control; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.harmony.tools.policytool.model.CommentEntry; +import org.apache.harmony.tools.policytool.model.KeystoreEntry; +import org.apache.harmony.tools.policytool.model.KeystorePasswordURLEntry; +import org.apache.harmony.tools.policytool.model.PolicyEntry; + +/** + * Policy text parser. + * + */ +public class PolicyTextParser { + + /** String containing the white spaces. */ + private static final String WHITE_SPACES = " \t\n\r"; + + /** + * Parses a policy text and creates an equivalent list of policy entries from it. + * @param policyText policy text to be parsed + * @return an equivalent list of policy entries created from the policy text, equivalent to policy text + * @throws InvalidPolicyTextException thrown if policyText is invalid + */ + public static List< PolicyEntry > parsePolicyText( String policyText ) throws InvalidPolicyTextException { + policyText = policyText.replace( "\r", "" ); // I only want to handle a unified line terminator + + final List< PolicyEntry > policyEntryList = new ArrayList< PolicyEntry >(); + + final char[] policyTextChars = policyText.toCharArray(); + + int index = 0; + int[] firstLastTokenIndices; + while ( ( firstLastTokenIndices = peekTokenAhead( policyTextChars, index ) ) != null ) { + + final String nextToken = policyText.substring( firstLastTokenIndices[ 0 ], firstLastTokenIndices[ 1 ] ); + final String loweredNextToken = nextToken.toLowerCase(); + + int newIndex = -1; + + // Line comment + if ( nextToken.startsWith( "//" ) ) { + newIndex = policyText.indexOf( '\n', firstLastTokenIndices[ 1 ] ); + if ( newIndex < 0 ) // This is the last line + newIndex = policyTextChars.length; + policyEntryList.add( new CommentEntry( policyText.substring( index, newIndex ) ) ); + } + + // Block comment + else if ( nextToken.startsWith( "/*" ) ) { + newIndex = policyText.indexOf( "*/", firstLastTokenIndices[ 1 ] ); + if ( newIndex < 0 ) // No closing bracket + throw new InvalidPolicyTextException( "No closing bracket for block comment!" ); + newIndex += 2; // length of "*/" + policyEntryList.add( new CommentEntry( policyText.substring( index, newIndex ) ) ); + } + + // Keystore entry + else if ( loweredNextToken.equals( KeystoreEntry.LOWERED_KEYWORD ) ) { + final int[] firstLastKeystoreURLIndices = peekQuotedStringAhead( policyTextChars, firstLastTokenIndices[ 1 ] ); + if ( firstLastKeystoreURLIndices == null ) + throw new InvalidPolicyTextException( "Incomplete keystore entry, found no quoted string for keystore URL!" ); + + int[] keystoreTypeIndices = null; + int[] keystoreProviderIndices = null; + + char nextChar = peekNextNonWhiteSpaceChar( policyTextChars, firstLastKeystoreURLIndices[ 1 ] ); + if ( nextChar != PolicyEntry.TERMINATOR_CHAR ) { + if ( nextChar != ',' ) + throw new InvalidPolicyTextException( "Was expecting semicolon but found something else!" ); + keystoreTypeIndices = peekQuotedStringAhead( policyTextChars, policyText.indexOf( ',', firstLastKeystoreURLIndices[ 1 ] ) + 1 ); + if ( keystoreTypeIndices == null ) + throw new InvalidPolicyTextException( "Incomplete keystore entry, found no quoted string for keystore type!" ); + + nextChar = peekNextNonWhiteSpaceChar( policyTextChars, keystoreTypeIndices[ 1 ] ); + if ( nextChar != PolicyEntry.TERMINATOR_CHAR ) { + if ( nextChar != ',' ) + throw new InvalidPolicyTextException( "Was expecting semicolon but found something else!" ); + keystoreProviderIndices = peekQuotedStringAhead( policyTextChars, policyText.indexOf( ',', keystoreTypeIndices[ 1 ] ) + 1 ); + if ( keystoreProviderIndices == null ) + throw new InvalidPolicyTextException( "Incomplete keystore entry, found no quoted string for keystore provider!" ); + + if ( peekNextNonWhiteSpaceChar( policyTextChars, keystoreProviderIndices[ 1 ] ) != PolicyEntry.TERMINATOR_CHAR ) + throw new InvalidPolicyTextException( "Was expecting semicolon but found something else!" ); + else + newIndex = policyText.indexOf( PolicyEntry.TERMINATOR_CHAR, keystoreProviderIndices[ 1 ] ) + 1; + } + else + newIndex = policyText.indexOf( PolicyEntry.TERMINATOR_CHAR, keystoreTypeIndices[ 1 ] ) + 1; + } + else + newIndex = policyText.indexOf( PolicyEntry.TERMINATOR_CHAR, firstLastKeystoreURLIndices[ 1 ] ) + 1; + + final KeystoreEntry keystoreEntry = new KeystoreEntry(); + keystoreEntry.setUrl( policyText.substring( firstLastKeystoreURLIndices[ 0 ] + 1, firstLastKeystoreURLIndices[ 1 ] - 1 ) ); + if ( keystoreTypeIndices != null ) + keystoreEntry.setType( policyText.substring( keystoreTypeIndices[ 0 ] + 1, keystoreTypeIndices[ 1 ] - 1 ) ); + if ( keystoreProviderIndices != null ) + keystoreEntry.setProvider( policyText.substring( keystoreProviderIndices[ 0 ] + 1, keystoreProviderIndices[ 1 ] - 1 ) ); + policyEntryList.add( keystoreEntry ); + } + + // Keystore password URL entry + else if ( loweredNextToken.equals( KeystorePasswordURLEntry.LOWERED_KEYWORD ) ) { + final int[] firstLastKeystorePasswordURLIndices = peekQuotedStringAhead( policyTextChars, firstLastTokenIndices[ 1 ] ); + if ( firstLastKeystorePasswordURLIndices == null ) + throw new InvalidPolicyTextException( "Incomplete keystore password URL entry, found no quoted string for keystore password URL!" ); + + final char nextChar = peekNextNonWhiteSpaceChar( policyTextChars, firstLastKeystorePasswordURLIndices[ 1 ] ); + if ( nextChar != PolicyEntry.TERMINATOR_CHAR ) + throw new InvalidPolicyTextException( "Was expecting semicolon but found something else!" ); + else + newIndex = policyText.indexOf( PolicyEntry.TERMINATOR_CHAR, firstLastKeystorePasswordURLIndices[ 1 ] ) + 1; + + final KeystorePasswordURLEntry keystorePasswordURLEntry = new KeystorePasswordURLEntry(); + keystorePasswordURLEntry.setUrl( policyText.substring( firstLastKeystorePasswordURLIndices[ 0 ] + 1, firstLastKeystorePasswordURLIndices[ 1 ] - 1 ) ); + policyEntryList.add( keystorePasswordURLEntry ); + } + + if ( newIndex >= 0 && newIndex < policyTextChars.length && policyTextChars[ newIndex ] == '\n' ) + newIndex++; + + index = newIndex; + } + + return policyEntryList; + } + + /** + * Skips the following whtie spaces starting from the specified index, + * and returns the next non-white space index or -1 if end of string reached. + * + * @param stringChars char array of the string to use + * @param index index to start from + * @return the next non-white space index or -1 if end of string reached + */ + private static int skipWhiteSpaces( final char[] stringChars, int index ) { + try { + while ( WHITE_SPACES.indexOf( stringChars[ index ] ) >= 0 ) + index++; + + return index; + } catch ( final ArrayIndexOutOfBoundsException aioobe ) { + return -1; + } + } + + /** + * Returns the first non-whitespace character starting from a given index or -1 if all the remaining characters are white spaces. + * @param stringChars char array of the string to use + * @param index index to start from + * @return the first non-whitespace character starting from a given index or -1 if all the remaining characters are white spaces + */ + private static char peekNextNonWhiteSpaceChar( final char[] stringChars, int index ) { + index = skipWhiteSpaces( stringChars, index ); + if ( index < 0 ) + return (char) -1; + else + return stringChars[ index ]; + } + + /** + * Returns the first (inclusive) and last index (exclusive) of the next token or null if no more tokens. + * @param stringChars char array of the string to use + * @param index index to start from + * @return the first (inclusive) and last index (exclusive) of the next token or null if no more tokens + */ + private static int[] peekTokenAhead ( final char[] stringChars, int index ) { + index = skipWhiteSpaces( stringChars, index ); + if ( index < 0 ) + return null; + + final int firstIndex = index; + + while ( index++ < stringChars.length ) + if ( index == stringChars.length || WHITE_SPACES.indexOf( stringChars[ index ] ) >= 0 ) + break; + + return new int[] { firstIndex, index }; + } + + /** + * Returns the first (inclusive) and last index (exclusive) of the next quoted string or null if found something else.
+ * The string denoted by the returned indices includes the quotes in the beginning and in the end of the quoted string. + * + * @param stringChars char array of the string to use + * @param index index to start from + * @return the first (inclusive) and last index (exclusive) of the next quoted string or null if found something else + * @throws InvalidPolicyTextException thrown if no quoted string found + */ + private static int[] peekQuotedStringAhead ( final char[] stringChars, int index ) throws InvalidPolicyTextException { + try { + index = skipWhiteSpaces( stringChars, index ); + if ( index < 0 ) + return null; + + if ( stringChars[ index ] != '"' ) + throw new InvalidPolicyTextException( "Could not find expected quoted string (missing opener quotation mark)!" ); + + final int firstIndex = index; + + while ( ++index <= stringChars.length ) { + if ( index == stringChars.length ) + throw new InvalidPolicyTextException( "Could not find expected quoted string (missing closer quotation mark)!" ); + + if ( stringChars[ index ] == '"' ) + break; + } + + return new int[] { firstIndex, index+1 }; // +1 because end index must be exclusive + + } catch ( final ArrayIndexOutOfBoundsException aioobe ) { + return null; + } + } + +} Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/InvalidPolicyTextException.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/InvalidPolicyTextException.java (revision 0) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/control/InvalidPolicyTextException.java (revision 0) @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.tools.policytool.control; + +/** + * Represents that operation with invalid policy text was attempted. + */ +public class InvalidPolicyTextException extends Exception { + + /** Line number where syntax error was found. */ + private int lineNumber; + + /** + * Creates a new InvalidPolicyTextException. + * @param message error message + */ + public InvalidPolicyTextException( final String message ) { + super( message ); + } + + /** + * Creates a new InvalidPolicyTextException. + * @param message error message + * @param lineNumber line number where syntax error was found + */ + public InvalidPolicyTextException( final String message, final int lineNumber ) { + super( message ); + this.lineNumber = lineNumber; + } + + /** + * Returns the line number where syntax error was found. + * @return the line number where syntax error was found + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Sets the line number where syntax error was found. + * @param lineNumber the line number where syntax error was found + */ + public void setLineNumber( final int lineNumber ) { + this.lineNumber = lineNumber; + } + +} Index: modules/tools/src/main/java/org/apache/harmony/tools/policytool/Consts.java =================================================================== --- modules/tools/src/main/java/org/apache/harmony/tools/policytool/Consts.java (revision 681611) +++ modules/tools/src/main/java/org/apache/harmony/tools/policytool/Consts.java (working copy) @@ -17,6 +17,8 @@ package org.apache.harmony.tools.policytool; +import java.awt.Font; + /** * Holds general and application-wide constants. */ @@ -36,5 +38,12 @@ /** Font size in the direct editing panel. */ public static final int DIRECT_EDITING_FONT_SIZE = 13; + /** Font in the direct editing panel. */ + public static final Font DIRECT_EDITING_FONT = new Font( "Courier New", Font.PLAIN, Consts.DIRECT_EDITING_FONT_SIZE ); + /** Tab size in the direct editing panel. */ + public static final int DIRECT_EDITING_TAB_SIZE = 4; + + /** Proper line separator for the running operating system. */ + public static final String NEW_LINE_STRING = System.getProperty( "line.separator" ); }