changeset 1276:e09b5c8bf3cb

PolicyEditor updated to use sun.security.provider.PolicyParser * NEWS: mention new PolicyEditor support for SignedBy and Principals * netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditor.java: UI updates to support SignedBy and Principals * netx/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewer.java: likewise * netx/net/sourceforge/jnlp/security/policyeditor/PermissionActions.java: use diamond operator where applicable * netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorController.java: refactor to support use of PolicyIdentifiers rather than plain codebases * netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissions.java: replace fromString with fromPermissionEntry, fix formatting * netx/net/sourceforge/jnlp/security/policyeditor/PolicyEntry.java: implement Serializable and Transferable to enable clipboard support. Use Builder pattern. Support PolicyIdentifiers rather than only plain codebases * netx/net/sourceforge/jnlp/security/policyeditor/PolicyFileModel.java: refactor to support PolicyIdentifiers and use PolicyParser * netx/net/sourceforge/jnlp/security/dialogs/TemporaryPermissionsButton.java: use PolicyIdentifier to identify current applet by its codebase alone * netx/net/sourceforge/jnlp/util/docprovider/PolicyEditorTextsProvider.java: remove -codebase from PolicyEditor help text * netx/net/sourceforge/jnlp/OptionsDefinitions.java: add SIGNEDBY and PRINCIPALS for PolicyEditor, define CODEBASE to take exactly one argument * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewerTest.java: use PolicyIdentifier rather than codebase string * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorControllerTest.java: redo to use PolicyIdentifiers, DisplayablePermissions * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorParsingTest.java: use PolicyIdentifier rather than codebase string * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java (testActionsRegex, testTargetRegex, testRegexesAgainstBadPermissionNames): removed, PolicyParser has replaced this * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorTest.java: use PolicyIdentifier rather than codebase string * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEntryTest.java: use DisplayablePermission and PolicyEntry rather than CustomPermission * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyFileModelTest.java: use PolicyIdentifier rather than codebase string * netx/net/sourceforge/jnlp/security/policyeditor/KeystoreInfo.java: new file * netx/net/sourceforge/jnlp/security/policyeditor/PolicyIdentifier.java: new file * netx/net/sourceforge/jnlp/security/policyeditor/CustomPermission.java: removed * netx/net/sourceforge/jnlp/security/policyeditor/InvalidPolicyException.java: removed * tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPermissionTest.java: removed * netx/net/sourceforge/jnlp/resources/Messages.properties: many changes to PolicyEditor messages for key bindings, UI messages, error messages, etc. * netx/net/sourceforge/jnlp/resources/Messages_cs.properties: likewise * netx/net/sourceforge/jnlp/resources/Messages_de.properties: likewise * netx/net/sourceforge/jnlp/resources/Messages_pl.properties: likewise
author Andrew Azores <aazores@redhat.com>
date Thu, 30 Jul 2015 10:57:21 -0400
parents f53190734752
children c4e9fb236625
files ChangeLog NEWS netx/net/sourceforge/jnlp/OptionsDefinitions.java netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/resources/Messages_cs.properties netx/net/sourceforge/jnlp/resources/Messages_de.properties netx/net/sourceforge/jnlp/resources/Messages_pl.properties netx/net/sourceforge/jnlp/security/dialogs/TemporaryPermissionsButton.java netx/net/sourceforge/jnlp/security/policyeditor/CustomPermission.java netx/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewer.java netx/net/sourceforge/jnlp/security/policyeditor/InvalidPolicyException.java netx/net/sourceforge/jnlp/security/policyeditor/KeystoreInfo.java netx/net/sourceforge/jnlp/security/policyeditor/PermissionActions.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditor.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorController.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissions.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyEntry.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyFileModel.java netx/net/sourceforge/jnlp/security/policyeditor/PolicyIdentifier.java netx/net/sourceforge/jnlp/util/docprovider/PolicyEditorTextsProvider.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPermissionTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewerTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorControllerTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorParsingTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEntryTest.java tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyFileModelTest.java
diffstat 29 files changed, 1668 insertions(+), 1650 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jul 29 18:08:16 2015 +0200
+++ b/ChangeLog	Thu Jul 30 10:57:21 2015 -0400
@@ -63,6 +63,63 @@
 	* tests/reproducers/custom/UnsignedContentInMETAINF/srcs/Makefile: same
 
 
+2015-07-27  Andrew Azores  <aazores@redhat.com>
+
+	PolicyEditor updated to use sun.security.provider.PolicyParser
+	* NEWS: mention new PolicyEditor support for SignedBy and Principals
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditor.java: UI
+	updates to support SignedBy and Principals
+	* netx/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewer.java:
+	likewise
+	* netx/net/sourceforge/jnlp/security/policyeditor/PermissionActions.java:
+	use diamond operator where applicable
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorController.java:
+	refactor to support use of PolicyIdentifiers rather than plain codebases
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissions.java:
+	replace fromString with fromPermissionEntry, fix formatting
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyEntry.java:
+	implement Serializable and Transferable to enable clipboard support. Use
+	Builder pattern. Support PolicyIdentifiers rather than only plain codebases
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyFileModel.java:
+	refactor to support PolicyIdentifiers and use PolicyParser
+	* netx/net/sourceforge/jnlp/security/dialogs/TemporaryPermissionsButton.java:
+	use PolicyIdentifier to identify current applet by its codebase alone
+	* netx/net/sourceforge/jnlp/util/docprovider/PolicyEditorTextsProvider.java:
+	remove -codebase from PolicyEditor help text
+	* netx/net/sourceforge/jnlp/OptionsDefinitions.java: add SIGNEDBY and
+	PRINCIPALS for PolicyEditor, define CODEBASE to take exactly one argument
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewerTest.java:
+	use PolicyIdentifier rather than codebase string
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorControllerTest.java:
+	redo to use PolicyIdentifiers, DisplayablePermissions
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorParsingTest.java:
+	use PolicyIdentifier rather than codebase string
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java
+	(testActionsRegex, testTargetRegex,
+	testRegexesAgainstBadPermissionNames): removed, PolicyParser has replaced
+	this
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorTest.java:
+	use PolicyIdentifier rather than codebase string
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEntryTest.java:
+	use DisplayablePermission and PolicyEntry rather than CustomPermission
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyFileModelTest.java:
+	use PolicyIdentifier rather than codebase string
+	* netx/net/sourceforge/jnlp/security/policyeditor/KeystoreInfo.java: new
+	file
+	* netx/net/sourceforge/jnlp/security/policyeditor/PolicyIdentifier.java:
+	new file
+	* netx/net/sourceforge/jnlp/security/policyeditor/CustomPermission.java:
+	removed
+	* netx/net/sourceforge/jnlp/security/policyeditor/InvalidPolicyException.java:
+	removed
+	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPermissionTest.java:
+	removed
+	* netx/net/sourceforge/jnlp/resources/Messages.properties: many changes to
+	PolicyEditor messages for key bindings, UI messages, error messages, etc.
+	* netx/net/sourceforge/jnlp/resources/Messages_cs.properties: likewise
+	* netx/net/sourceforge/jnlp/resources/Messages_de.properties: likewise
+	* netx/net/sourceforge/jnlp/resources/Messages_pl.properties: likewise
+
 2015-07-23  Jiri Vanek  <jvanek@redhat.com>
 
 	Enabled and properly tested Entry-Point attribute check
--- a/NEWS	Wed Jul 29 18:08:16 2015 +0200
+++ b/NEWS	Thu Jul 30 10:57:21 2015 -0400
@@ -30,15 +30,16 @@
   - fixed issue with -html receiving garbage in width and height
 * PolicyEditor
   - file flag made to work when used standalone
+  - support for SignedBy and Principals along with existing Codebase
 
 New in release 1.6 (2015-XX-XX):
 * Massively improved offline abilities. Added Xoffline switch to force work without inet connection.
 * Improved to be able to run with any JDK
 * JDK 6 and older no longer supported
 * JDK 8 support added (URLPermission granted if applicable)
-* JDK 9 supported 
+* JDK 9 supported
 * Added support for Entry-Point manifest attribute
-* Added KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK deployment property to control scan of Manifest file 
+* Added KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK deployment property to control scan of Manifest file
 * starting arguments now accept also -- abbreviations
 * Added new documentation
 * Added support for menu shortcuts - both javaws applications/applets and html applets are supported
@@ -202,7 +203,7 @@
 
 New in release 1.1 (2011-XX-XX):
 * Security updates
-  - S6983554, CVE-2010-4450: Launcher incorrect processing of empty library path entries 
+  - S6983554, CVE-2010-4450: Launcher incorrect processing of empty library path entries
   - RH677332, CVE-2011-0706: IcedTea multiple signers privilege escalation
 * New Features
   - IcedTea-Web now installs to a FHS-compliant location
--- a/netx/net/sourceforge/jnlp/OptionsDefinitions.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/OptionsDefinitions.java	Thu Jul 30 10:57:21 2015 -0400
@@ -86,7 +86,9 @@
         //policyeditor
         //-help
         FILE("-file", "policy_file", "PBOFile", NumberOfArguments.ONE),
-        CODEBASE("-codebase", "url", "PBOCodebase", NumberOfArguments.ONE_OR_MORE);
+        CODEBASE("-codebase", "url", "PBOCodebase", NumberOfArguments.ONE),
+        SIGNEDBY("-signedby", "certificate_alias", "PBOSignedBy", NumberOfArguments.ONE),
+        PRINCIPALS("-principals", "class_name principal_name", "PBOPrincipals", NumberOfArguments.EVEN_NUMBER_SUPPORTS_EQUALS_CHAR);
 
         public final String option;
 
@@ -175,6 +177,8 @@
             OPTIONS.HELP1,
             OPTIONS.FILE,
             OPTIONS.CODEBASE,
+            OPTIONS.SIGNEDBY,
+            OPTIONS.PRINCIPALS,
             OPTIONS.VERBOSE
             }
         );
--- a/netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/controlpanel/PolicyPanel.java	Thu Jul 30 10:57:21 2015 -0400
@@ -166,6 +166,7 @@
     private void launchSimplePolicyEditor(final String filePath) {
         if (policyEditor == null || policyEditor.getPolicyEditor().isClosed()) {
             policyEditor = PolicyEditor.getPolicyEditorFrame(filePath);
+            policyEditor.getPolicyEditor().openAndParsePolicyFile();
             policyEditor.asWindow().setVisible(true);
         } else {
             policyEditor.asWindow().toFront();
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Thu Jul 30 10:57:21 2015 -0400
@@ -356,10 +356,13 @@
 IBOCheck=Checks that all the current settings have valid values.
 
 PBOFile=Specifies a policy file path to open. If exactly one argument is given, and it is not this flag, it is interpreted as a file path to open, as if this flag was given first. This flag exists \
-mostly for compatibility with Policy Tool, but is also needed when opening a policy file and also using the -codebase flag.
-PBOCodebase=Specifies an applet codebase URL. If the specified codebase already exists in the policy file (if any), then it will be selected when the editor opens. If it is a new codebase then it will \
-be added and selected. Multiple URLs may also be given with a single -codebase flag by separating them with spaces. In this case, the last codebase given will be selected, and all will  be \
-added. If this flag is given more than once, only the first is used.
+mostly for compatibility with Policy Tool.
+PBOCodebase=Specifies an applet codebase URL. This can be used with the other selector options to select a policy entry upon opening the editor; \
+if no such identifier exists then it will be created and then selected.
+PBOSignedBy=Specifies a certificate alias for a certificate stored in the keystore. This can be used with the other selector options to select a policy entry upon opening the editor; \
+if no such identifier exists then it will be created and then selected.
+PBOPrincipals=Specifies class name/principal name pairs (space-separated) for the policy entry identifier. This can be used with the other selector options to select a policy entry upon opening the editor; \
+if no such identifier exists then it will be created and then selected.
 
 # Option Parser
 OPUnevenParams=For option {0} expected an even number of params.
@@ -722,14 +725,17 @@
 PEGetEnvDetail=Allow applets to read system environment variables
 PECouldNotOpen=Unable to open policy file
 PECouldNotSave=Unable to save policy file
-PEAddCodebase=Add new Codebase
-PERemoveCodebase=Remove
-PECodebasePrompt=Enter a new codebase
+PEAddEntry=Add New...
+PERemoveEntry=Remove
+PEEntryPrompt=Enter a new policy entry identifier
+PEAddPrincipal=Add Principal
+PERemovePrincipal=Remove Principal
+PEEditPrincipal=Edit Principal
 PEGlobalSettings=All Applets
 PESaveChanges=Save changes before exiting?
 PEChangesSaved=Changes saved
 PECheckboxLabel=Permissions
-PECodebaseLabel=Codebases
+PEEntriesLabel=Entries
 PEFileMenu=File
 PENewMenuItem=New
 PEOpenMenuItem=Open...
@@ -737,26 +743,38 @@
 PESaveMenuItem=Save
 PESaveAsMenuItem=Save As...
 PEExitMenuItem=Exit
-PECodebaseMenu=Codebase
-PEAddCodebaseItem=Add New...
-PERemoveCodebaseItem=Remove
-PERenameCodebaseItem=Rename
-PECopyCodebaseItem=Copy
-PEPasteCodebaseItem=Paste...
-PERenameCodebase=Rename codebase to:
-PEPasteCodebase=Paste copied codebase as:
+PEEntryMenu=Entry
+PEAddEntryItem=Add New...
+PERemoveEntryItem=Remove
+PEModifySubmenuItem=Modify
+PEModifyCodebaseItem=Codebase
+PEModifySignedByItem=Signed By
+PEModifyPrincipalsItem=Principals
+PECopyEntryItem=Copy
+PEPasteEntryItem=Paste...
+PEModifyCodebase=Change Codebase to:
+PEModifyPrincipals=Modify Principals:
+PEModifySignedBy=Changed SignedBy to:
+PEPasteEntry=Paste copied entry with codebase:
 PEViewMenu=View
 PECustomPermissionsItem=Custom Permissions...
+PECodebaseInputLabel=Codebase URL:
+PESignedByInputLabel=SignedBy:
+PEPrincipalsInputLabel=Principals:
+PEPrincipalClassNameInputLabel=Class Name:
+PEPrincipalPrincipalNameInputLabel=Principal Name:
 PEFileModified=File Modification Warning
 PEFileModifiedDetail=The policy file at {0} has been modified since it was opened. Reload and re-edit before saving?
 PEFileMissing=The policy file was missing from disk. A new file has been saved with the same name.
-PEGAccesUnowenedCode = Execute unowned code
-PEGMediaAccess = Media access
-PEGrightClick = right click to fold/unfold
-PEGReadFileSystem = Read from system
-PEGWriteFileSystem = Write to system
+PEGAccessUnownedCode=Execute unowned code
+PEGMediaAccess=Media access
+PEGRightClick=Right click to fold/unfold
+PEGReadFileSystem=Read from system
+PEGWriteFileSystem=Write to system
 PEClipboardError=Clipboard does not appear to contain properly formatted policy entries
-PEInvalidPolicy=Paste Failed: Could not read policy entry for codebase {0} from system clipboard
+PEInvalidPolicy=Paste Failed: Could not read policy entry for entry {0} from system clipboard
+PEInvalidIdentifier=Please fill in/modify at least one of the fields.
+PEIdentifierMatchesAll=Please fill in/modify at least one of the fields.
 PEClipboardAccessError=Could not read from clipboard
 
 PEHelpMenu=Help
@@ -799,31 +817,33 @@
 
 # PolicyEditor key mnemonics. See KeyEvent.VK_*
 PEFileMenuMnemonic=F
-PECodebaseMenuMnemonic=C
+PEEntryMenuMnemonic=E
 PEViewMenuMnemonic=V
 PEHelpMenuMnemonic=H
 
-PEAddCodebaseMnemonic=N
-PEAddCodebaseItemMnemonic=N
-PERemoveCodebaseMnemonic=R
-PERemoveCodebaseItemMnemonic=R
+PEAddEntryMnemonic=N
+PEAddEntryItemMnemonic=N
+PERemoveEntryMnemonic=R
+PERemoveEntryItemMnemonic=R
 PENewMenuItemMnemonic=N
 PEOpenMenuItemMnemonic=O
 PEOpenDefaultMenuItemMnemonic=D
 PESaveMenuItemMnemonic=S
 PEExitMenuItemMnemonic=X
 PECustomPermissionsItemMnemonic=U
-PECopyCodebaseItemMnemonic=C
-PEPasteCodebaseItemMnemonic=P
-PECopyCodebaseToClipboardItemMnemonic=U
+PECopyEntryItemMnemonic=C
+PEPasteEntryItemMnemonic=P
 PESaveAsMenuItemMnemonic=A
-PERenameCodebaseItemMnemonic=E
+PEModifySubmenuItemMnemonic=M
+PEModifyEntryCodebaseItemMnemonic=B
+PEModifyEntryPrincipalsItemMnemonic=I
+PEModifyEntrySignedByItemMnemonic=G
 PEAboutPolicyEditorItemMnemonic=A
 PEPolicyEditorHelpItemMnemonic=H
 
 # See javax.swing.KeyStroke.getKeyStroke(String)
-PEAddCodebaseItemAccelerator=control shift N
-PERemoveCodebaseItemAccelerator=DELETE
+PEAddEntryItemAccelerator=control shift N
+PERemoveEntryItemAccelerator=DELETE
 PENewMenuItemAccelerator=control N
 PEOpenMenuItemAccelerator=control O
 PEOpenDefaultMenuItemAccelerator=control D
@@ -831,10 +851,11 @@
 PESaveAsMenuItemAccelerator=control shift S
 PEExitMenuItemAccelerator=control Q
 PECustomPermissionsItemAccelerator=control U
-PECopyCodebaseItemAccelerator=control C
-PEPasteCodebaseItemAccelerator=control V
-PERenameCodebaseItemAccelerator=F2
-PECopyCodebaseToClipboardItemAccelerator=control shift C
+PECopyEntryItemAccelerator=control C
+PEPasteEntryItemAccelerator=control V
+PEModifyEntryCodebaseItemAccelerator=F2
+PEModifyEntryPrincipalsItemAccelerator=shift F2
+PEModifyEntrySignedByItemAccelerator=control F2
 PEAboutPolicyEditorCloseAccelerator=control Q
 
 #conole itself labels
--- a/netx/net/sourceforge/jnlp/resources/Messages_cs.properties	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/resources/Messages_cs.properties	Thu Jul 30 10:57:21 2015 -0400
@@ -675,14 +675,14 @@
 PEGetEnvDetail=Umo\u017en\u00ed aplet\u016fm p\u0159\u00edstup ke \u010dten\u00ed prom\u011bnn\u00fdch syst\u00e9mov\u00e9ho prost\u0159ed\u00ed.
 PECouldNotOpen=Nelze otev\u0159\u00edt soubor se z\u00e1sadami
 PECouldNotSave=Nelze ulo\u017eit soubor se z\u00e1sadami
-PEAddCodebase=P\u0159idat novou z\u00e1kladnu k\u00f3du (codebase)
-PERemoveCodebase=Odstranit
-PECodebasePrompt=Zadejte novou z\u00e1kladnu k\u00f3du (codebase)
+PEAddEntry=P\u0159idat novou z\u00e1kladnu k\u00f3du (codebase)
+PERemoveEntry=Odstranit
+PEEntryPrompt=Zadejte novou z\u00e1kladnu k\u00f3du (codebase)
 PEGlobalSettings=V\u0161echny aplety
 PESaveChanges=Ulo\u017eit zm\u011bny p\u0159ed ukon\u010den\u00edm?
 PEChangesSaved=Zm\u011bny byly ulo\u017eeny.
 PECheckboxLabel=Opr\u00e1vn\u011bn\u00ed
-PECodebaseLabel=Z\u00e1kladny k\u00f3du (codebases)
+PEEntriesLabel=Z\u00e1kladny k\u00f3du (codebases)
 PEFileMenu=Soubor
 PENewMenuItem=Nov\u00fd
 PEOpenMenuItem=Otev\u0159\u00edt...
@@ -690,22 +690,22 @@
 PESaveMenuItem=Ulo\u017eit
 PESaveAsMenuItem=Ulo\u017eit jako...
 PEExitMenuItem=Ukon\u010dit
-PECodebaseMenu=Z\u00e1kladna k\u00f3du (codebase)
-PEAddCodebaseItem=P\u0159idat novou...
-PERemoveCodebaseItem=Odstranit
-PERenameCodebaseItem=P\u0159ejmenovat
-PECopyCodebaseItem=Kop\u00edrovat
-PEPasteCodebaseItem=Vlo\u017eit...
-PERenameCodebase=P\u0159ejmenovat z\u00e1kladnu k\u00f3du (codebase) na:
-PEPasteCodebase=Vlo\u017eit kop\u00edrovanou z\u00e1kladnu k\u00f3du (codebase) jako:
+PEEntryMenu=Z\u00e1kladna k\u00f3du (codebase)
+PEAddEntryItem=P\u0159idat novou...
+PERemoveEntryItem=Odstranit
+PEModifyCodebaseItem=P\u0159ejmenovat
+PECopyEntryItem=Kop\u00edrovat
+PEPasteEntryItem=Vlo\u017eit...
+PEModifyCodebase=P\u0159ejmenovat z\u00e1kladnu k\u00f3du (codebase) na:
+PEPasteEntry=Vlo\u017eit kop\u00edrovanou z\u00e1kladnu k\u00f3du (codebase) jako:
 PEViewMenu=Zobrazit
 PECustomPermissionsItem=Vlastn\u00ed opr\u00e1vn\u011bn\u00ed
 PEFileModified=Varov\u00e1n\u00ed ohledn\u011b zm\u011bny souboru
 PEFileModifiedDetail=Soubor se z\u00e1sadami v um\u00edst\u011bn\u00ed {0} byl od posledn\u00edho otev\u0159en\u00ed zm\u011bn\u011bn. Chcete ho p\u0159ed ulo\u017een\u00edm znovu na\u010d\u00edst a upravit?
 PEFileMissing=Soubor se z\u00e1sadami na disku chyb\u011bl. Byl ulo\u017een nov\u00fd soubor se stejn\u00fdm n\u00e1zvem.
-PEGAccesUnowenedCode= Spou\u0161t\u011bn\u00ed k\u00f3du, kter\u00fd nevlastn\u00edte
+PEGAccessUnownedCode= Spou\u0161t\u011bn\u00ed k\u00f3du, kter\u00fd nevlastn\u00edte
 PEGMediaAccess= P\u0159\u00edstup k m\u00e9di\u00edm
-PEGrightClick= Prav\u00fdm tla\u010d\u00edtkem my\u0161i rozbalit/sbalit nab\u00eddku
+PEGRightClick= Prav\u00fdm tla\u010d\u00edtkem my\u0161i rozbalit/sbalit nab\u00eddku
 PEGReadFileSystem= \u010cten\u00ed ze syst\u00e9mu
 PEGWriteFileSystem= Zapisov\u00e1n\u00ed do syst\u00e9mu
 PEClipboardError=Zd\u00e1 se, \u017ee schr\u00e1nka neobsahuje pat\u0159i\u010dn\u011b form\u00e1tovan\u00e9 polo\u017eky z\u00e1sad.
@@ -731,31 +731,31 @@
 
 # PolicyEditor key mnemonics. See KeyEvent.VK_*
 PEFileMenuMnemonic=F
-PECodebaseMenuMnemonic=C
+PEEntryMenuMnemonic=C
 PEViewMenuMnemonic=V
 PEHelpMenuMnemonic=H
 
-PEAddCodebaseMnemonic=N
-PEAddCodebaseItemMnemonic=N
-PERemoveCodebaseMnemonic=R
-PERemoveCodebaseItemMnemonic=R
+PEAddEntryMnemonic=N
+PEAddEntryItemMnemonic=N
+PERemoveEntryMnemonic=R
+PERemoveEntryItemMnemonic=R
 PENewMenuItemMnemonic=N
 PEOpenMenuItemMnemonic=O
 PEOpenDefaultMenuItemMnemonic=D
 PESaveMenuItemMnemonic=S
 PEExitMenuItemMnemonic=X
 PECustomPermissionsItemMnemonic=U
-PECopyCodebaseItemMnemonic=C
-PEPasteCodebaseItemMnemonic=P
+PECopyEntryItemMnemonic=C
+PEPasteEntryItemMnemonic=P
 PECopyCodebaseToClipboardItemMnemonic=U
 PESaveAsMenuItemMnemonic=A
-PERenameCodebaseItemMnemonic=E
+PEModifyEntryCodebaseItemMnemonic=E
 PEAboutPolicyEditorItemMnemonic=A
 PEPolicyEditorHelpItemMnemonic=H
 
 # See javax.swing.KeyStroke.getKeyStroke(String)
-PEAddCodebaseItemAccelerator=ctrl shift N
-PERemoveCodebaseItemAccelerator=DEL
+PEAddEntryItemAccelerator=ctrl shift N
+PERemoveEntryItemAccelerator=DEL
 PENewMenuItemAccelerator=ctrl N
 PEOpenMenuItemAccelerator=ctrl O
 PEOpenDefaultMenuItemAccelerator=ctrl D
@@ -763,9 +763,9 @@
 PESaveAsMenuItemAccelerator=ctrl shift S
 PEExitMenuItemAccelerator=ctrl Q
 PECustomPermissionsItemAccelerator=ctrl U
-PECopyCodebaseItemAccelerator=ctrl C
-PEPasteCodebaseItemAccelerator=ctrl V
-PERenameCodebaseItemAccelerator=F2
+PECopyEntryItemAccelerator=ctrl C
+PEPasteEntryItemAccelerator=ctrl V
+PEModifyEntryCodebaseItemAccelerator=F2
 PECopyCodebaseToClipboardItemAccelerator=ctrl shift C
 PEAboutPolicyEditorCloseAccelerator=ctrl Q
 
--- a/netx/net/sourceforge/jnlp/resources/Messages_de.properties	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/resources/Messages_de.properties	Thu Jul 30 10:57:21 2015 -0400
@@ -561,14 +561,14 @@
 PEGetEnvDetail=Applets das Lesen von Systemumgebungsvariablen erlauben
 PECouldNotOpen=Kann die Richtliniendatei nicht \u00f6ffnen
 PECouldNotSave=Kann die Richtliniendatei nicht speichern
-PEAddCodebase=Neue Codebasis hinzuf\u00fcgen
-PERemoveCodebase=Entfernen
-PECodebasePrompt=Eine neue Codebasis eingeben:
+PEAddEntry=Neue Codebasis hinzuf\u00fcgen
+PERemoveEntry=Entfernen
+PEEntryPrompt=Eine neue Codebasis eingeben:
 PEGlobalSettings=Alle Applets
 PESaveChanges=Sollen die \u00c4nderungen vor dem Beenden gespeichert werden?
 PEChangesSaved=\u00c4nderungen gespeichert
 PECheckboxLabel=Berechtigungen
-PECodebaseLabel=Codebasen
+PEEntriesLabel=Codebasen
 PEFileMenu=Datei
 PEOpenMenuItem=\u00d6ffnen...
 PESaveMenuItem=Speichern
@@ -578,9 +578,9 @@
 PECustomPermissionsItem=Benutzerdefinierte Berechtigungen...
 PEFileModified=Datei\u00e4nderungswarnung
 PEFileModifiedDetail=Die Richtliniendatei \u201e{0}\u201c wurde ge\u00e4ndert seit sie ge\u00f6ffnet wurde.\nNeu laden und bearbeiten vor dem Speichern?
-PEGAccesUnowenedCode=Fremden Code ausf\u00fchren
+PEGAccessUnownedCode=Fremden Code ausf\u00fchren
 PEGMediaAccess=Medienzugriff
-PEGrightClick=Rechtsklick zum auf-/zuklappen
+PEGRightClick=Rechtsklick zum auf-/zuklappen
 PEGReadFileSystem=Zum System lesen
 PEGWriteFileSystem=Zum System schreiben
 
@@ -597,9 +597,9 @@
 
 # PolicyEditor key mnemonics. See java.awt.event.KeyEvent.VK_*
 # N
-PEAddCodebaseMnemonic=78
+PEAddEntryMnemonic=78
 # E
-PERemoveCodebaseMnemonic=69
+PERemoveEntryMnemonic=69
 # O
 PEOkButtonMnemonic=79
 # A
--- a/netx/net/sourceforge/jnlp/resources/Messages_pl.properties	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/resources/Messages_pl.properties	Thu Jul 30 10:57:21 2015 -0400
@@ -558,33 +558,33 @@
 PEGetEnvDetail=Zezwala aplet-om na odczyt zmiennych \u015brodowiskowych
 PECouldNotOpen=Nie mo\u017cna otworzy\u0107 pliku wytycznej
 PECouldNotSave=Nie mo\u017cna zapisa\u0107 pliku wytycznej
-PEAddCodebase=Dodaj baz\u0119 kodu
-PERemoveCodebase=Usu\u0144
-PECodebasePrompt=Podaj now\u0105 baz\u0119 kodu
+PEAddEntry=Dodaj baz\u0119 kodu
+PERemoveEntry=Usu\u0144
+PEEntryPrompt=Podaj now\u0105 baz\u0119 kodu
 PEGlobalSettings=Wszystkie Applet-y
 PESaveChanges=Zapisa\u0107 zmiany przed zako\u0144czeniem?
 PEChangesSaved=Zapisano zmiany
 PECheckboxLabel=Uprawniena
-PECodebaseLabel=Bazy kodu
+PEEntriesLabel=Bazy kodu
 PEFileMenu=Plik
 PEOpenMenuItem=Otw\u00f3rz...
 PESaveMenuItem=Zapisz
 PESaveAsMenuItem=Zapisz\u00a0jako...
 PEExitMenuItem=Zako\u0144cz
 PEEditMenu=Edycja
-PERenameCodebaseItem=Przemianuj baz\u0119 kodu
-PECopyCodebaseItem=Kopiuj baz\u0119 kodu
-PEPasteCodebaseItem=Wklej baz\u0119 kodu
+PEModifyCodebaseItem=Przemianuj baz\u0119 kodu
+PECopyEntryItem=Kopiuj baz\u0119 kodu
+PEPasteEntryItem=Wklej baz\u0119 kodu
 PECopyCodebaseToClipboardItem=Kopiuj URL bazy kodu do schowka
-PERenameCodebase=Przemianowana baza kodu:
-PEPasteCodebase=Skopiowana baza kodu:
+PEModifyCodebase=Przemianowana baza kodu:
+PEPasteEntry=Skopiowana baza kodu:
 PEViewMenu=Widok
 PECustomPermissionsItem=Uprawnienia\u00a0dostosowane...
 PEFileModified=Ostrze\u017cenie o modyfikacji pliku
 PEFileModifiedDetail=Plik wytycznej \u201e{0}\u201d zosta\u0142 zmodyfikowany od jego otwarcia. Czy chcesz go za\u0142adowa\u0107 ponownie?
-PEGAccesUnowenedCode=Wykonanie obcego kodu
+PEGAccessUnownedCode=Wykonanie obcego kodu
 PEGMediaAccess=Dost\u0119p do medi\u00f3w
-PEGrightClick=Kliknij prawym klawiszem myszy, aby ro/zwin\u0105\u0107
+PEGRightClick=Kliknij prawym klawiszem myszy, aby ro/zwin\u0105\u0107
 PEGReadFileSystem=Odczyt w system
 PEGWriteFileSystem=Zapis w system
 
--- a/netx/net/sourceforge/jnlp/security/dialogs/TemporaryPermissionsButton.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/dialogs/TemporaryPermissionsButton.java	Thu Jul 30 10:57:21 2015 -0400
@@ -48,6 +48,7 @@
 import java.net.URL;
 import java.security.Permission;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 
 import javax.swing.AbstractButton;
@@ -62,7 +63,9 @@
 import net.sourceforge.jnlp.security.policyeditor.PolicyEditor;
 import net.sourceforge.jnlp.security.policyeditor.PolicyEditor.PolicyEditorWindow;
 import net.sourceforge.jnlp.security.policyeditor.PolicyEditorPermissions;
+import net.sourceforge.jnlp.security.policyeditor.PolicyIdentifier;
 import net.sourceforge.jnlp.util.logging.OutputController;
+import sun.security.provider.PolicyParser;
 
 public class TemporaryPermissionsButton extends JButton {
 
@@ -169,12 +172,13 @@
 
             if (policyEditorWindow == null || policyEditorWindow.getPolicyEditor().isClosed()) {
                 policyEditorWindow = PolicyEditor.getPolicyEditorDialog(filepath);
+                policyEditorWindow.getPolicyEditor().openPolicyFileSynchronously();
             } else {
                 policyEditorWindow.asWindow().toFront();
                 policyEditorWindow.asWindow().repaint();
             }
             policyEditorWindow.setModalityType(ModalityType.DOCUMENT_MODAL);
-            policyEditorWindow.getPolicyEditor().addNewCodebase(file.getCodeBase().toString());
+            policyEditorWindow.getPolicyEditor().addNewEntry(new PolicyIdentifier(null, Collections.<PolicyParser.PrincipalEntry>emptySet(), file.getCodeBase().toString()));
             policyEditorWindow.asWindow().setVisible(true);
             menu.setVisible(false);
         }
--- a/netx/net/sourceforge/jnlp/security/policyeditor/CustomPermission.java	Wed Jul 29 18:08:16 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/*Copyright (C) 2014 Red Hat, Inc.
-
-This file is part of IcedTea.
-
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
-
-IcedTea 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 GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
- */
-
-package net.sourceforge.jnlp.security.policyeditor;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This class models a permission entry in a policy file which is not included
- * in the default set of permissions used by the PolicyEditor, ie, permissions
- * not defined in the enum PolicyEditorPermissions.
- */
-public class CustomPermission implements Comparable<CustomPermission> {
-
-    /* Matches eg 'permission java.io.FilePermission "${user.home}${/}*", "read";'
-     * eg permissions that have a permission type, target, and actions set.
-     */
-    public static final Pattern ACTIONS_PERMISSION =
-            Pattern.compile("\\s*permission\\s+([\\w\\.]+)\\s+\"([^\"]+)\",\\s*\"([^\"]*)\";.*");
-
-    /* Matches eg 'permission java.lang.RuntimePermission "queuePrintJob";'
-     * eg permissions that have a permission type and target, but no actions.
-     */
-    public static final Pattern TARGET_PERMISSION =
-            Pattern.compile("\\s*permission\\s+([\\w\\.]+)\\s+\"([^\"]+)\";.*");
-
-    public final String type, target, actions;
-
-    /**
-     * Constructor for custom basic permissions (ie those without actions). This is equivalent
-     * to CustomPermission(type, target, "")
-     * @param type eg java.io.FilePermission
-     * @param target eg ${user.home}${/}*
-     */
-    public CustomPermission(final String type, final String target) {
-        this(type, target, "");
-    }
-
-    /**
-     * Constructor for custom permissions with actions
-     * @param type eg java.io.FilePermission
-     * @param target eg ${user.home}${/}*
-     * @param actions eg read,write
-     */
-    public CustomPermission(final String type, final String target, final String actions) {
-        this.type = type;
-        this.target = target;
-        this.actions = actions;
-    }
-
-    public CustomPermission(final PermissionType type, final PermissionTarget target) {
-        this(type, target, PermissionActions.NONE);
-    }
-
-    public CustomPermission(final PermissionType type, final PermissionTarget target, final PermissionActions actions) {
-        this(type.type, target.target, actions.rawString());
-    }
-
-    /**
-     * Get a CustomPermission from a policy file permission entry string. This is the full
-     * entry string, eg `permission java.io.FilePermission "${user.home}${/}* "read";`
-     * @param string the permission entry string
-     * @return a CustomPermission representing this string
-     */
-    public static CustomPermission fromString(final String string) {
-        final String typeStr, targetStr, actionsStr;
-
-        final Matcher actionMatcher = ACTIONS_PERMISSION.matcher(string);
-        if (actionMatcher.matches()) {
-            typeStr = actionMatcher.group(1);
-            targetStr = actionMatcher.group(2);
-            actionsStr = actionMatcher.group(3);
-        } else {
-            final Matcher targetMatcher = TARGET_PERMISSION.matcher(string);
-            if (!targetMatcher.matches()) {
-                return null;
-            }
-            typeStr = targetMatcher.group(1);
-            targetStr = targetMatcher.group(2);
-            actionsStr = "";
-        }
-
-        return new CustomPermission(typeStr, targetStr, actionsStr);
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("permission ");
-        sb.append(type);
-        sb.append(" \"");
-        sb.append(target);
-        sb.append("\"");
-
-        if (!this.actions.equals(PermissionActions.NONE.rawString())) {
-            sb.append(", \"");
-            sb.append(actions);
-            sb.append("\"");
-        }
-
-        sb.append(";");
-
-        return sb.toString();
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (!(o instanceof CustomPermission)) return false;
-
-        final CustomPermission that = (CustomPermission) o;
-
-        if (!actions.equals(that.actions)) return false;
-        if (!target.equals(that.target)) return false;
-        if (!type.equals(that.type)) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = type.hashCode();
-        result = 31 * result + target.hashCode();
-        result = 31 * result + actions.hashCode();
-        return result;
-    }
-
-    @Override
-    public int compareTo(final CustomPermission o) {
-        if (this == o) {
-            return 0;
-        }
-        final int typeComparison = this.type.compareTo(o.type);
-        if (typeComparison != 0) {
-            return typeComparison;
-        }
-        final int targetComparison = this.target.compareTo(o.target);
-        if (targetComparison != 0) {
-            return targetComparison;
-        }
-        final int actionsComparison = this.actions.compareTo(o.actions);
-        if (actionsComparison != 0) {
-            return actionsComparison;
-        }
-        return 0;
-    }
-}
--- a/netx/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewer.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewer.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,6 +36,8 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
+import sun.security.provider.PolicyParser;
+
 import static net.sourceforge.jnlp.runtime.Translator.R;
 
 import java.awt.GridBagConstraints;
@@ -67,31 +69,32 @@
  */
 public class CustomPolicyViewer extends JFrame {
 
-    private final Collection<CustomPermission> customPermissions = new TreeSet<>();
+    private final Collection<DisplayablePermission> customPermissions = new TreeSet<>();
     private final JScrollPane scrollPane = new JScrollPane();
-    private final DefaultListModel<CustomPermission> listModel = new DefaultListModel<>();
-    private final JList<CustomPermission> list = new JList<>(listModel);
+    private final DefaultListModel<DisplayablePermission> listModel = new DefaultListModel<>();
+    private final JList<DisplayablePermission> list = new JList<>(listModel);
     private final JButton addButton = new JButton(), removeButton = new JButton(), closeButton = new JButton();
     private final JLabel listLabel = new JLabel();
     private final ActionListener addButtonAction, removeButtonAction, closeButtonAction;
     private final PolicyEditor parent;
-    private final String codebase;
+    private final PolicyIdentifier policyIdentifier;
 
     /**
      * @param parent the parent PolicyEditor which created this CustomPolicyViewer
-     * @param codebase the codebase for which these custom permissions are enabled
-     * @param policyFile the PolicyFileModel
+     * @param policyIdentifier the policy identifier for which these custom permissions are enabled
      */
-    public CustomPolicyViewer(final PolicyEditor parent, final String codebase) {
+    public CustomPolicyViewer(final PolicyEditor parent, final PolicyIdentifier policyIdentifier) {
         super();
         Objects.requireNonNull(parent);
-        Objects.requireNonNull(codebase);
+        Objects.requireNonNull(policyIdentifier);
         this.parent = parent;
-        this.codebase = codebase;
+        this.policyIdentifier = policyIdentifier;
         setLayout(new GridBagLayout());
         setTitle(R("PECPTitle"));
-        customPermissions.addAll(parent.getCustomPermissions(codebase));
-        for (final CustomPermission perm : customPermissions) {
+        for (final PolicyParser.PermissionEntry permissionEntry : parent.getCustomPermissions(policyIdentifier)) {
+            customPermissions.add(DisplayablePermission.from(permissionEntry));
+        }
+        for (final DisplayablePermission perm : customPermissions) {
             listModel.addElement(perm);
         }
 
@@ -115,7 +118,7 @@
                 } else {
                     actions = "";
                 }
-                final CustomPermission perm = new CustomPermission(type, target, actions);
+                final PolicyParser.PermissionEntry perm = new PolicyParser.PermissionEntry(type, target, actions);
                 addCustomPermission(perm);
             }
         };
@@ -125,7 +128,7 @@
         removeButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                final CustomPermission selected = list.getSelectedValue();
+                final PolicyParser.PermissionEntry selected = list.getSelectedValue();
                 if (selected == null) {
                     return;
                 }
@@ -144,13 +147,7 @@
         closeButton.setText(R("PECPCloseButton"));
         closeButton.addActionListener(closeButtonAction);
 
-        final String codebaseText;
-        if (codebase.trim().isEmpty()) {
-            codebaseText = R("PEGlobalSettings");
-        } else {
-            codebaseText = codebase;
-        }
-        listLabel.setText(R("PECPListLabel", codebaseText));
+        listLabel.setText(R("PECPListLabel", policyIdentifier.toStringNoHtml()));
 
         scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
         scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
@@ -215,35 +212,83 @@
     /**
      * Update the custom permissions map. Used by the Custom Policy Viewer to update its parent
      * PolicyEditor to changes it has made
-     * @param codebase the codebase for which changes were made
-     * @param permissions the permissions granted to this codebase
      */
     private void updateCustomPermissions() {
         parent.setChangesMade(true);
-        parent.clearCustomPermissions(codebase);
-        for (final CustomPermission permission : customPermissions) {
-            parent.addCustomPermission(codebase, permission);
+        parent.clearCustomPermissions(policyIdentifier);
+        for (final PolicyParser.PermissionEntry permission : customPermissions) {
+            parent.addCustomPermission(policyIdentifier, permission);
         }
     }
 
-    void addCustomPermission(final CustomPermission permission) {
+    void addCustomPermission(final PolicyParser.PermissionEntry permission) {
         Objects.requireNonNull(permission);
-        if (customPermissions.add(permission)) {
-            listModel.addElement(permission);
+        final DisplayablePermission displayablePermission = DisplayablePermission.from(permission);
+        if (customPermissions.add(displayablePermission)) {
+            listModel.addElement(displayablePermission);
             updateCustomPermissions();
         }
         list.setSelectedValue(permission, true);
     }
 
-    void removeCustomPermission(final CustomPermission permission) {
+    void removeCustomPermission(final PolicyParser.PermissionEntry permission) {
         Objects.requireNonNull(permission);
-        customPermissions.remove(permission);
+        customPermissions.remove(DisplayablePermission.from(permission));
         listModel.removeElement(permission);
         updateCustomPermissions();
     }
 
-    Collection<CustomPermission> getCopyOfCustomPermissions() {
+    Collection<DisplayablePermission> getCopyOfCustomPermissions() {
         return new TreeSet<>(customPermissions);
     }
 
+    public static class DisplayablePermission extends PolicyParser.PermissionEntry implements Comparable<PolicyParser.PermissionEntry> {
+
+        public DisplayablePermission(final PermissionType type, final PermissionTarget target) {
+            this(type, target, PermissionActions.NONE);
+        }
+
+        public DisplayablePermission(final PermissionType type, final PermissionTarget target, final PermissionActions actions) {
+            this(type.type, target.target, actions.rawString());
+        }
+
+        public DisplayablePermission(final String type, final String target) {
+            this(type, target, null);
+        }
+
+        public DisplayablePermission(final String permission, final String name, final String action) {
+            super(permission, name, action);
+        }
+
+        public static DisplayablePermission from(final PolicyParser.PermissionEntry permissionEntry) {
+            return new DisplayablePermission(permissionEntry.permission, permissionEntry.name, permissionEntry.action);
+        }
+
+        @Override
+        public int compareTo(final PolicyParser.PermissionEntry o) {
+            return super.name.compareTo(o.name);
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("permission ");
+            sb.append(permission);
+            sb.append(" \"");
+            sb.append(name);
+            sb.append("\"");
+
+            if (this.action == null || !this.action.trim().equals(PermissionActions.NONE.rawString())) {
+                sb.append(", \"");
+                sb.append(action);
+                sb.append("\"");
+            }
+
+            sb.append(";");
+
+            return sb.toString();
+        }
+
+    }
+
 }
--- a/netx/net/sourceforge/jnlp/security/policyeditor/InvalidPolicyException.java	Wed Jul 29 18:08:16 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*Copyright (C) 2014 Red Hat, Inc.
-
-This file is part of IcedTea.
-
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
-
-IcedTea 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 GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
- */
-
-package net.sourceforge.jnlp.security.policyeditor;
-
-public class InvalidPolicyException extends Exception {
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/KeystoreInfo.java	Thu Jul 30 10:57:21 2015 -0400
@@ -0,0 +1,110 @@
+/*Copyright (C) 2014 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea 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 GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.security.policyeditor;
+
+import java.net.URL;
+import java.util.Objects;
+
+// http://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
+public class KeystoreInfo {
+
+    private final String keyStoreUrl;
+    private final String keyStoreType;
+    private final String keyStoreProvider;
+    private final String keyStorePasswordUrl;
+
+    public KeystoreInfo(final String keyStoreUrl, final String keyStoreType, final String keyStoreProvider, final String keyStorePasswordUrl) {
+        this.keyStoreUrl = keyStoreUrl;
+        this.keyStoreType = keyStoreType;
+        this.keyStoreProvider = keyStoreProvider;
+        this.keyStorePasswordUrl = keyStorePasswordUrl;
+    }
+
+    public String getKeyStoreUrl() {
+        return keyStoreUrl;
+    }
+
+    public String getKeyStoreType() {
+        return keyStoreType;
+    }
+
+    public String getKeyStoreProvider() {
+        return keyStoreProvider;
+    }
+
+    public String getKeyStorePasswordUrl() {
+        return keyStorePasswordUrl;
+    }
+
+    @Override
+    public String toString() {
+        return "KeystoreInfo{" +
+            "keyStoreUrl='" + keyStoreUrl + '\'' +
+            ", keyStoreType='" + keyStoreType + '\'' +
+            ", keyStoreProvider='" + keyStoreProvider + '\'' +
+            ", keyStorePasswordUrl='" + keyStorePasswordUrl + '\'' +
+        '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final KeystoreInfo that = (KeystoreInfo) o;
+
+        if (!keyStorePasswordUrl.equals(that.keyStorePasswordUrl)) {
+            return false;
+        }
+        if (!keyStoreProvider.equals(that.keyStoreProvider)) {
+            return false;
+        }
+        if (!keyStoreType.equals(that.keyStoreType)) {
+            return false;
+        }
+        if (!keyStoreUrl.equals(that.keyStoreUrl)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(keyStoreUrl,  keyStoreType, keyStoreProvider, keyStorePasswordUrl);
+    }
+}
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PermissionActions.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PermissionActions.java	Thu Jul 30 10:57:21 2015 -0400
@@ -84,7 +84,7 @@
     }
 
     public Collection<String> getActions() {
-        return new HashSet<String>(this.actions);
+        return new HashSet<>(this.actions);
     }
 
     private static Set<String> setFromString(final String string) {
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditor.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditor.java	Thu Jul 30 10:57:21 2015 -0400
@@ -59,9 +59,13 @@
 import java.lang.ref.WeakReference;
 import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -72,6 +76,7 @@
 import javax.swing.AbstractButton;
 import javax.swing.Action;
 import javax.swing.ActionMap;
+import javax.swing.BoxLayout;
 import javax.swing.DefaultListModel;
 import javax.swing.InputMap;
 import javax.swing.JButton;
@@ -87,6 +92,7 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.ListSelectionModel;
 import javax.swing.ScrollPaneConstants;
@@ -98,11 +104,13 @@
 import javax.swing.border.LineBorder;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
+
 import net.sourceforge.jnlp.about.AboutDialog;
 import net.sourceforge.jnlp.OptionsDefinitions;
 import net.sourceforge.jnlp.config.PathsAndFiles;
 import net.sourceforge.jnlp.runtime.JNLPRuntime;
 
+import net.sourceforge.jnlp.runtime.Translator;
 import net.sourceforge.jnlp.security.policyeditor.PolicyEditorPermissions.Group;
 import net.sourceforge.jnlp.util.FileUtils;
 import net.sourceforge.jnlp.util.FileUtils.OpenFileResult;
@@ -111,62 +119,22 @@
 import net.sourceforge.jnlp.util.docprovider.formatters.formatters.PlainTextFormatter;
 import net.sourceforge.jnlp.util.logging.OutputController;
 import net.sourceforge.jnlp.util.optionparser.OptionParser;
+import sun.security.provider.PolicyParser;
 
 /**
  * This class provides a policy editing tool as a simpler alternate to
- * the JDK PolicyTool. It is much simpler than PolicyTool - only
- * a handful of pre-defined permissions can be enabled or disabled,
- * on a per-codebase basis. There are no considerations for Principals,
- * who signed the code, or custom permissions.
- * 
- * This editor has a very simple idea of a policy file's contents. If any
- * entries are found which it does not recognize, eg 'grant' blocks which
- * have more than zero or one simple codeBase attributes, or 'Principal'
- * or other attributes assigned to the "grant block", or any other type
- * of complication to a "grant block" beyond a single codebase,
- * then all of these pieces of data are disregarded. When the editor saves
- * its work, all of this unrecognized data will be overwritten. Since
- * the editor has no way to display any of these contents anyway, it would
- * be potentially dangerous to allow this information to persist in the
- * policy file even after it has been edited and saved, as this would mean
- * the policy file contents may not be what the user thinks they are.
- * 
- * Comments in policy files are loosely supported, using both block-style
- * comment delimiters and double slashes. Block comments may not, however,
- * be placed on a line with "functional" text on the same line. To be
- * safe, comments should not be adjacent to "functional" text in the file
- * unless those lines are intended to be disregarded, ie commented out.
- * Comments will *not* be preserved when PolicyEditor next saves to the
- * file.
+ * the JDK PolicyTool.
  */
 public class PolicyEditor extends JPanel {
 
-    /**
-     * Command line switch to print a help message.
-     */
-    public static final String HELP_FLAG = OptionsDefinitions.OPTIONS.HELP1.option;
-
-    /**
-     * Command line switch to specify the location of the policy file.
-     * If not given, then the default DeploymentConfiguration path is used.
-     */
-    public static final String FILE_FLAG = OptionsDefinitions.OPTIONS.FILE.option;
-
-    /**
-     * Command line switch to specify a new codebase entry to be made.
-     * Can only be used once, presently.
-     */
-    public static final String CODEBASE_FLAG = OptionsDefinitions.OPTIONS.CODEBASE.option;
-
-    
-      private boolean closed = false;
+    private boolean closed = false;
     private final Map<PolicyEditorPermissions, JCheckBox> checkboxMap = new TreeMap<>();
     private final List<JCheckBoxWithGroup> groupBoxList = new ArrayList<>(Group.values().length);
     private final JScrollPane scrollPane = new JScrollPane();
-    private final DefaultListModel<String> listModel = new DefaultListModel<>();
-    private final JList<String> list = new JList<>(listModel);
+    private final DefaultListModel<PolicyIdentifier> listModel = new DefaultListModel<>();
+    private final JList<PolicyIdentifier> list = new JList<>(listModel);
     private final JButton okButton = new JButton(), closeButton = new JButton(),
-            addCodebaseButton = new JButton(), removeCodebaseButton = new JButton();
+            addEntryButton = new JButton(), removeEntryButton = new JButton();
     private final JFileChooser fileChooser;
     private CustomPolicyViewer cpViewer = null;
     /**
@@ -178,9 +146,10 @@
     private final WeakReference<PolicyEditor> parentPolicyEditor = new WeakReference<>(this);
     public final PolicyEditorController policyEditorController = new PolicyEditorController();
 
-    private final ActionListener okButtonAction, addCodebaseButtonAction,
-            removeCodebaseButtonAction, newButtonAction, openButtonAction, openDefaultButtonAction, saveAsButtonAction, viewCustomButtonAction,
-            renameCodebaseButtonAction, copyCodebaseButtonAction, pasteCodebaseButtonAction,
+    private final ActionListener okButtonAction, addEntryButtonAction,
+            removeEntryButtonAction, newButtonAction, openButtonAction, openDefaultButtonAction, saveAsButtonAction, viewCustomButtonAction,
+            modifyCodebaseButtonAction, modifyPrincipalsButtonAction, modifySignedByButtonAction,
+            copyEntryButtonAction, pasteEntryButtonAction,
             policyEditorHelpButtonAction, aboutPolicyEditorButtonAction, aboutItwButtonAction;
     private final ActionListener closeButtonAction;
 
@@ -222,6 +191,83 @@
         }
     }
 
+    private static class PrincipalsPanel extends JPanel {
+
+        private final DefaultListModel<PolicyParser.PrincipalEntry> principals = new DefaultListModel<>();
+
+        public PrincipalsPanel(final Collection<PolicyParser.PrincipalEntry> entries) {
+            super();
+            setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
+            for (final PolicyParser.PrincipalEntry principalEntry : entries) {
+                principals.addElement(principalEntry);
+            }
+            final JList<PolicyParser.PrincipalEntry> principalsList = new JList<>(principals);
+            final JScrollPane scrollPane = new JScrollPane(principalsList);
+            final JButton addButton = new JButton(R("PEAddPrincipal"));
+            addButton.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(final ActionEvent e) {
+                    final JTextField className = new JTextField();
+                    final JTextField principalName = new JTextField();
+                    final int option = JOptionPane.showConfirmDialog(
+                            null,
+                            new Object[]{R("PEPrincipalClassNameInputLabel"), className, R("PEPrincipalPrincipalNameInputLabel"), principalName},
+                            R("PEAddPrincipal"),
+                            JOptionPane.OK_CANCEL_OPTION
+                    );
+                    if (option == JOptionPane.OK_OPTION) {
+                        final PolicyParser.PrincipalEntry entry = new PolicyParser.PrincipalEntry(className.getText(), principalName.getText());
+                        principals.addElement(entry);
+                    }
+                }
+            });
+            final JButton removeButton = new JButton(R("PERemovePrincipal"));
+            removeButton.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(final ActionEvent e) {
+                    principals.removeElement(principalsList.getSelectedValue());
+                }
+            });
+            final JButton editButton = new JButton(R("PEEditPrincipal"));
+            editButton.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(final ActionEvent e) {
+                    final PolicyParser.PrincipalEntry entry = principalsList.getSelectedValue();
+                    if (entry == null) {
+                        return;
+                    }
+                    final JTextField className = new JTextField();
+                    final JTextField principalName = new JTextField();
+                    className.setText(entry.getDisplayClass());
+                    principalName.setText(entry.getDisplayName());
+                    final int option = JOptionPane.showConfirmDialog(
+                        null,
+                        new Object[]{R("PEPrincipalClassNameInputLabel"), className, R("PEPrincipalPrincipalNameInputLabel"), principalName},
+                        R("PEEditPrincipal"),
+                        JOptionPane.OK_CANCEL_OPTION
+                    );
+                    if (option == JOptionPane.OK_OPTION) {
+                        principals.removeElement(entry);
+                        final PolicyParser.PrincipalEntry newEntry = new PolicyParser.PrincipalEntry(className.getText(), principalName.getText());
+                        principals.addElement(newEntry);
+                    }
+                }
+            });
+            add(scrollPane);
+            add(addButton);
+            add(editButton);
+            add(removeButton);
+        }
+
+        public List<PolicyParser.PrincipalEntry> getPrincipals() {
+            final List<PolicyParser.PrincipalEntry> entries = new ArrayList<>(principals.size());
+            for (final PolicyParser.PrincipalEntry entry : Collections.list(principals.elements())) {
+                entries.add(entry);
+            }
+            return entries;
+        }
+    }
+
     public PolicyEditor(final String filepath) {
         super();
         setLayout(new GridBagLayout());
@@ -234,39 +280,7 @@
         }
 
         setFile(filepath);
-        if (filepath != null) {
-            try {
-                policyEditorController.openAndParsePolicyFile();
-            } catch (final FileNotFoundException fnfe) {
-                OutputController.getLogger().log(fnfe);
-                FileUtils.showCouldNotOpenDialog(PolicyEditor.this, R("PECouldNotOpen"));
-            } catch (final IOException | InvalidPolicyException e) {
-                OutputController.getLogger().log(e);
-                OutputController.getLogger().log(OutputController.Level.ERROR_ALL, R("RCantOpenFile", policyEditorController.getFile().getPath()));
-                FileUtils.showCouldNotOpenDialog(PolicyEditor.this, R("PECouldNotOpen"));
-            }
-            try {
-                invokeRunnableOrEnqueueAndWait(new Runnable() {
-                    @Override
-                    public void run() {
-                        for (final String codebase : policyEditorController.getCodebases()) {
-                            final String model;
-                            if (codebase.isEmpty()) {
-                                model = R("PEGlobalSettings");
-                            } else {
-                                model = codebase;
-                            }
-                            if (!listModel.contains(model)) {
-                                listModel.addElement(model);
-                            }
-                        }
-                        addNewCodebase("");
-                    }
-                });
-            } catch (final InvocationTargetException | InterruptedException e) {
-                OutputController.getLogger().log(e);
-            }
-        }
+        addDefaultAllAppletsIdentifier();
         setChangesMade(false);
 
         fileChooser = new JFileChooser(policyEditorController.getFile());
@@ -284,6 +298,7 @@
 
                 // May still be null if user cancelled the file chooser
                 if (policyEditorController.getFile() != null) {
+                    setChangesMade(true);
                     savePolicyFile();
                 }
             }
@@ -291,28 +306,30 @@
         okButton.setText(R("ButApply"));
         okButton.addActionListener(okButtonAction);
 
-        addCodebaseButtonAction = new ActionListener() {
+        addEntryButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                addNewCodebaseInteractive();
+                addNewIdentifierInteractive();
             }
         };
-        addCodebaseButton.setText(R("PEAddCodebase"));
-        addCodebaseButton.addActionListener(addCodebaseButtonAction);
+        addEntryButton.setText(R("PEAddEntry"));
+        addEntryButton.addActionListener(addEntryButtonAction);
 
-        removeCodebaseButtonAction = new ActionListener() {
+        removeEntryButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                removeCodebase(getSelectedCodebase());
+                removeIdentifier(getSelectedPolicyIdentifier());
             }
         };
-        removeCodebaseButton.setText(R("PERemoveCodebase"));
-        removeCodebaseButton.addActionListener(removeCodebaseButtonAction);
+        removeEntryButton.setText(R("PERemoveEntry"));
+        removeEntryButton.addActionListener(removeEntryButtonAction);
 
         newButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                if (!promptOnSaveChangesMade(false)) return;
+                if (!promptOnSaveChangesMade(false)) {
+                    return;
+                }
                 setFile(null);
                 setChangesMade(false);
             }
@@ -321,7 +338,9 @@
         openButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                if (!promptOnSaveChangesMade(true)) return;
+                if (!promptOnSaveChangesMade(true)) {
+                    return;
+                }
                 final int choice = fileChooser.showOpenDialog(PolicyEditor.this);
                 if (choice == JFileChooser.APPROVE_OPTION) {
                     PolicyEditor.this.setFile(fileChooser.getSelectedFile().getAbsolutePath());
@@ -330,12 +349,20 @@
             }
         };
         
-         openDefaultButtonAction = new ActionListener() {
+        openDefaultButtonAction = new ActionListener() {
             @Override
-            public void actionPerformed(final ActionEvent e) {
-                if (!promptOnSaveChangesMade(true)) return;
-                    PolicyEditor.this.setFile(PathsAndFiles.JAVA_POLICY.getFullPath());
-                    openAndParsePolicyFile();
+            public void actionPerformed(final ActionEvent event) {
+                if (!promptOnSaveChangesMade(true)) {
+                    return;
+                }
+                try {
+                    PolicyEditor.this.setFile(new File(new URI(PathsAndFiles.JAVA_POLICY.getFullPath())).getAbsolutePath());
+                    PolicyEditor.this.getFile().createNewFile();
+                } catch (final IOException | URISyntaxException e) {
+                    OutputController.getLogger().log(e);
+                    return;
+                }
+                openAndParsePolicyFile();
             }
         };
 
@@ -351,59 +378,77 @@
             }
         };
 
-        renameCodebaseButtonAction = new ActionListener() {
+        modifyCodebaseButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                final String oldCodebase = getSelectedCodebase();
-                if (oldCodebase.isEmpty()) {
+                if (getSelectedPolicyIdentifier().equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
                     return;
                 }
-                String newCodebase = "";
-                while (!validateCodebase(newCodebase) || policyEditorController.getCopyOfPermissions().containsKey(newCodebase)) {
-                    newCodebase = JOptionPane.showInputDialog(PolicyEditor.this, R("PERenameCodebase"), oldCodebase);
+                final String oldCodebase = getSelectedPolicyIdentifier().getCodebase();
+                String newCodebase;
+                do {
+                    newCodebase = JOptionPane.showInputDialog(PolicyEditor.this, R("PEModifyCodebase"), oldCodebase);
                     if (newCodebase == null) {
                         return;
                     }
-                }
-                renameCodebase(oldCodebase, newCodebase);
-            }
-        };
-
-        copyCodebaseButtonAction = new ActionListener() {
-            @Override
-            public void actionPerformed(final ActionEvent e) {
-                copyCodebase(getSelectedCodebase());
+                } while (!validateCodebase(newCodebase));
+                modifyCodebase(getSelectedPolicyIdentifier(), newCodebase);
             }
         };
 
-        pasteCodebaseButtonAction = new ActionListener() {
+        modifyPrincipalsButtonAction = new ActionListener() {
+            @Override
+            public void actionPerformed(final ActionEvent e) {
+                if (getSelectedPolicyIdentifier().equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
+                    return;
+                }
+                final PrincipalsPanel panel = new PrincipalsPanel(getSelectedPolicyIdentifier().getPrincipals());
+                final int option = JOptionPane.showConfirmDialog(
+                    null,
+                    new Object[]{ R("PEPrincipalsInputLabel"), panel },
+                    R("PEEntryPrompt"),
+                    JOptionPane.OK_CANCEL_OPTION
+                );
+                if (option == JOptionPane.OK_OPTION) {
+                    modifyPrincipals(getSelectedPolicyIdentifier(), panel.getPrincipals());
+                }
+            }
+        };
+
+        modifySignedByButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                String clipboardCodebase = null;
+                if (getSelectedPolicyIdentifier().equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
+                    return;
+                }
+                final String newSignedBy = JOptionPane.showInputDialog(PolicyEditor.this, R("PEModifySignedBy"), getSelectedPolicyIdentifier().getSignedBy());
+                if (newSignedBy == null) {
+                    return;
+                }
+                modifySignedBy(getSelectedPolicyIdentifier(), newSignedBy);
+            }
+        };
+
+        copyEntryButtonAction = new ActionListener() {
+            @Override
+            public void actionPerformed(final ActionEvent e) {
+                copyEntry(getSelectedPolicyIdentifier());
+            }
+        };
+
+        pasteEntryButtonAction = new ActionListener() {
+            @Override
+            public void actionPerformed(final ActionEvent e) {
+                PolicyIdentifier identifier = null;
                 try {
-                    clipboardCodebase = policyEditorController.getCodebaseFromClipboard();
-                    final String newCodebase;
-                    if (getCodebases().contains(clipboardCodebase)) {
-                        String cb = "";
-                        while (!validateCodebase(cb) || policyEditorController.getCopyOfPermissions().containsKey(cb)) {
-                            cb = JOptionPane.showInputDialog(PolicyEditor.this, R("PEPasteCodebase"), "http://");
-                            if (cb == null) {
-                                return;
-                            }
-                        }
-                        newCodebase = cb;
-                    } else {
-                        newCodebase = clipboardCodebase;
-                    }
-                    if (validateCodebase(newCodebase)) {
-                        pasteCodebase(newCodebase);
-                    }
+                    identifier = PolicyEditorController.getPolicyEntryFromClipboard().getPolicyIdentifier();
+                    pasteEntry(promptForPolicyIdentifier(identifier));
                 } catch (final UnsupportedFlavorException ufe) {
                     OutputController.getLogger().log(ufe);
                     showClipboardErrorDialog();
-                } catch (final InvalidPolicyException ipe) {
-                    OutputController.getLogger().log(ipe);
-                    showInvalidPolicyExceptionDialog(clipboardCodebase);
+                } catch (final PolicyParser.ParsingException pe) {
+                    OutputController.getLogger().log(pe);
+                    showInvalidPolicyExceptionDialog(identifier);
                 } catch (final IOException ioe) {
                     OutputController.getLogger().log(ioe);
                     showCouldNotAccessClipboardDialog();
@@ -417,12 +462,12 @@
                 invokeRunnableOrEnqueueLater(new Runnable() {
                     @Override
                     public void run() {
-                        String codebase = getSelectedCodebase();
-                        if (codebase == null) {
+                        final PolicyIdentifier policyIdentifier = getSelectedPolicyIdentifier();
+                        if (policyIdentifier == null) {
                             return;
                         }
                         if (cpViewer == null) {
-                            cpViewer = new CustomPolicyViewer(PolicyEditor.this, codebase);
+                            cpViewer = new CustomPolicyViewer(PolicyEditor.this, policyIdentifier);
                             cpViewer.setVisible(true);
                         } else {
                             cpViewer.toFront();
@@ -444,11 +489,11 @@
             @Override
             public void actionPerformed(final ActionEvent e) {
                 boolean modal = getModality();
-                AboutDialog.display(modal, TextsProvider.POLICY_EDITOR,AboutDialog.ShowPage.HELP);
+                AboutDialog.display(modal, TextsProvider.POLICY_EDITOR, AboutDialog.ShowPage.HELP);
             }
         };
         
-         aboutItwButtonAction = new ActionListener() {
+        aboutItwButtonAction = new ActionListener() {
             @Override
             public void actionPerformed(final ActionEvent e) {
                 boolean modal = getModality();
@@ -470,7 +515,11 @@
 
         setupLayout();
     }
-    
+
+    private void addDefaultAllAppletsIdentifier() {
+        addNewEntry(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
+    }
+
     private boolean getModality() {
         boolean modal = false;
         Container parent = PolicyEditor.this;
@@ -497,7 +546,7 @@
         if (policyEditorController.changesMade()) {
             final int save = JOptionPane.showConfirmDialog(this, R("PESaveChanges"));
             if (save == JOptionPane.YES_OPTION) {
-                if (policyEditorController.getFile() == null) {
+                if (getFile() == null) {
                     final int choice = fileChooser.showSaveDialog(this);
                     if (choice == JFileChooser.APPROVE_OPTION) {
                         this.setFile(fileChooser.getSelectedFile().getAbsolutePath());
@@ -520,14 +569,14 @@
         }
         return true;
     }
-
+    
     public void setFile(final String filepath) {
         if (filepath != null) {
             policyEditorController.setFile(new File(filepath));
         } else {
             policyEditorController.setFile(null);
-            resetCodebases();
-            addNewCodebase("");
+            resetEntries();
+            addDefaultAllAppletsIdentifier();
         }
         setParentWindowTitle(getWindowTitleForStatus());
     }
@@ -569,15 +618,8 @@
         return result;
     }
 
-    private String getSelectedCodebase() {
-        final String codebase = list.getSelectedValue();
-        if (codebase == null || codebase.isEmpty()) {
-            return null;
-        }
-        if (codebase.equals(R("PEGlobalSettings"))) {
-            return "";
-        }
-        return codebase;
+    private PolicyIdentifier getSelectedPolicyIdentifier() {
+        return list.getSelectedValue();
     }
 
     private static void preparePolicyEditorWindow(final PolicyEditorWindow w, final PolicyEditor e) {
@@ -602,23 +644,23 @@
         });
     }
 
-    public static interface PolicyEditorWindow {
+    public interface PolicyEditorWindow {
 
-        public void setTitle(String s);
+        void setTitle(String s);
 
-        public void setDefaultCloseOperation(int i);
+        void setDefaultCloseOperation(int i);
 
-        public PolicyEditor getPolicyEditor();
+        PolicyEditor getPolicyEditor();
 
-        public void setPolicyEditor(PolicyEditor e);
+        void setPolicyEditor(PolicyEditor e);
 
-        public void setJMenuBar(JMenuBar menu);
+        void setJMenuBar(JMenuBar menu);
 
-        public Window asWindow();
+        Window asWindow();
 
-        public void setModalityType(ModalityType modalityType);
+        void setModalityType(ModalityType modalityType);
 
-        public void quit();
+        void quit();
     }
 
     private static class PolicyEditorFrame extends JFrame implements PolicyEditorWindow {
@@ -627,7 +669,7 @@
 
         private PolicyEditorFrame(final PolicyEditor editor) {
             super();
-            preparePolicyEditorWindow((PolicyEditorWindow) this, editor);
+            preparePolicyEditorWindow(this, editor);
         }
 
         @Override
@@ -714,7 +756,7 @@
 
         private PolicyEditorDialog(final PolicyEditor editor) {
             super();
-            preparePolicyEditorWindow((PolicyEditorWindow) this, editor);
+            preparePolicyEditorWindow(this, editor);
         }
 
         @Override
@@ -783,36 +825,39 @@
     }
 
     /**
-     * Add a new codebase to the editor's model. If the codebase is not a valid URL,
-     * the codebase is not added.
-     * @param codebase to be added
+     * Add a new identifier to the editor's model.
+     * @param identifier to be added
      */
-    public void addNewCodebase(final String codebase) {
-        if (!codebase.isEmpty() && !validateCodebase(codebase)) {
-            OutputController.getLogger().log("Could not add codebase " + codebase);
+    public void addNewEntry(final PolicyIdentifier identifier) {
+        if (!validateCodebase(identifier.getCodebase())) {
             return;
         }
-        final String model;
-        if (codebase.isEmpty()) {
-            model = R("PEGlobalSettings");
-        } else {
-            model = codebase;
-        }
-        policyEditorController.addCodebase(codebase);
+        policyEditorController.addIdentifier(identifier);
         invokeRunnableOrEnqueueLater(new Runnable() {
             @Override
             public void run() {
-                if (!listModel.contains(model)) {
-                    listModel.addElement(model);
-                    setChangesMade(true);
+                for (final PolicyIdentifier identifier : policyEditorController.getIdentifiers()) {
+                    if (!listModel.contains(identifier)) {
+                        listModel.addElement(identifier);
+                    }
                 }
-                list.setSelectedValue(model, true);
-                updateCheckboxes(codebase);
+                list.setSelectedValue(identifier, true);
+                updateCheckboxes(identifier);
             }
         });
     }
 
+    static PolicyIdentifier identifierFromCodebase(final String codebase) {
+        if (codebase.isEmpty() || codebase.equals(Translator.R("PEGlobalSettings"))) {
+            return PolicyIdentifier.ALL_APPLETS_IDENTIFIER;
+        }
+        return new PolicyIdentifier(null, Collections.<PolicyParser.PrincipalEntry>emptyList(), codebase);
+    }
+
     private static boolean validateCodebase(final String codebase) {
+        if (codebase == null || codebase.isEmpty()) {
+            return true;
+        }
         try {
             new URL(codebase);
         } catch (final MalformedURLException mue) {
@@ -821,128 +866,180 @@
         return true;
     }
 
-
     public File getFile() {
         return policyEditorController.getFile();
     }
 
     /**
-     * Display an input dialog, which will disappear when the user enters a valid URL
-     * or when the user presses cancel. If an invalid URL is entered, the dialog reappears.
-     * When a valid URL is entered, it is used to create a new codebase entry in the editor's
-     * policy file model.
+     * Display an input dialog, which will disappear when the user enters a valid entry
+     * or when the user presses cancel.
      */
-    public void addNewCodebaseInteractive() {
+    public void addNewIdentifierInteractive() {
         invokeRunnableOrEnqueueLater(new Runnable() {
             @Override
             public void run() {
-                String codebase = "";
-                while (!validateCodebase(codebase)) {
-                    codebase = JOptionPane.showInputDialog(PolicyEditor.this, R("PECodebasePrompt"), "http://");
-                    if (codebase == null) {
-                        return;
-                    }
+                final PolicyIdentifier identifier = promptForPolicyIdentifier(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
+                if (identifier == null) {
+                    return;
                 }
-                addNewCodebase(codebase);
+                addNewEntry(identifier);
             }
         });
     }
 
+    private PolicyIdentifier promptForPolicyIdentifier(final PolicyIdentifier initialValues) {
+        PolicyIdentifier identifier = initialValues;
+        final JTextField codebase = new JTextField();
+        final JTextField signedBy = new JTextField();
+        final PrincipalsPanel principalsPanel = new PrincipalsPanel(Collections.<PolicyParser.PrincipalEntry>emptySet());
+        while (identifier.equals(initialValues) || identifier.equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER) || !validateCodebase(codebase.getText())) {
+            codebase.setText(identifier.getCodebase());
+            signedBy.setText(identifier.getSignedBy());
+            final int option = JOptionPane.showConfirmDialog(
+                    PolicyEditor.this,
+                    new Object[]{R("PECodebaseInputLabel"), codebase, R("PEPrincipalsInputLabel"), principalsPanel, R("PESignedByInputLabel"), signedBy},
+                    R("PEEntryPrompt"),
+                    JOptionPane.OK_CANCEL_OPTION
+            );
+            if (option == JOptionPane.OK_OPTION) {
+                final String cb = codebase.getText().trim().isEmpty() ? null : codebase.getText().trim();
+                final String sb = signedBy.getText().trim().isEmpty() ? null : signedBy.getText().trim();
+                identifier = new PolicyIdentifier(sb, principalsPanel.getPrincipals(), cb);
+                if (identifier.equals(initialValues)) {
+                    JOptionPane.showMessageDialog(null, R("PEInvalidIdentifier"));
+                } else if (identifier.equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
+                    JOptionPane.showMessageDialog(null, R("PEIdentifierMatchesAll"));
+                }
+            } else {
+                return null;
+            }
+        }
+        return identifier;
+    }
+
     /**
-     * Remove a codebase from the editor's model
-     * @param codebase to be removed
+     * Remove an identifier from the editor's model
+     * @param identifier to be removed
      */
-    public void removeCodebase(final String codebase) {
-        if (codebase.equals(R("PEGlobalSettings")) || codebase.isEmpty()) {
+    public void removeIdentifier(final PolicyIdentifier identifier) {
+        if (identifier.equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
             return;
         }
         int previousIndex = list.getSelectedIndex() - 1;
         if (previousIndex < 0) {
             previousIndex = 0;
         }
-        policyEditorController.removeCodebase(codebase);
+        policyEditorController.removeIdentifier(identifier);
         final int fIndex = previousIndex;
         invokeRunnableOrEnqueueLater(new Runnable() {
             @Override
             public void run() {
-                listModel.removeElement(codebase);
+                listModel.removeElement(identifier);
                 list.setSelectedIndex(fIndex);
             }
         });
         setChangesMade(true);
     }
 
-    /**
-     * Rename a codebase, preserving its permissions
-     * @param oldCodebase the codebase to rename
-     * @param newCodebase the new name for the codebase
-     */
-    public void renameCodebase(final String oldCodebase, final String newCodebase) {
-        final Map<PolicyEditorPermissions, Boolean> permissions = getPermissions(oldCodebase);
-        final Collection<CustomPermission> customPermissions = getCustomPermissions(oldCodebase);
+    public void modifyCodebase(final PolicyIdentifier identifier, final String newCodebase) {
+        final PolicyIdentifier newIdentifier = new PolicyIdentifier(identifier.getSignedBy(), identifier.getPrincipals(), newCodebase);
+        replaceIdentifier(identifier, newIdentifier);
+    }
+
+    public void modifyPrincipals(final PolicyIdentifier identifier, final List<PolicyParser.PrincipalEntry> principalEntries) {
+        final PolicyIdentifier newIdentifier = new PolicyIdentifier(identifier.getSignedBy(), principalEntries, identifier.getCodebase());
+        replaceIdentifier(identifier, newIdentifier);
+    }
 
-        removeCodebase(oldCodebase);
-        addNewCodebase(newCodebase);
+    public void modifySignedBy(final PolicyIdentifier identifier, final String newSignedBy) {
+        final PolicyIdentifier newIdentifier = new PolicyIdentifier(newSignedBy, identifier.getPrincipals(), identifier.getCodebase());
+        replaceIdentifier(identifier, newIdentifier);
+    }
+
+    private void replaceIdentifier(final PolicyIdentifier oldIdentifier, final PolicyIdentifier newIdentifier) {
+        if (oldIdentifier.equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER) || newIdentifier.equals(PolicyIdentifier.ALL_APPLETS_IDENTIFIER)) {
+            return;
+        }
+
+        final Map<PolicyEditorPermissions, Boolean> permissions = getPermissions(oldIdentifier);
+        final Collection<PolicyParser.PermissionEntry> customPermissions = getCustomPermissions(oldIdentifier);
+
+        removeIdentifier(oldIdentifier);
+        addNewEntry(newIdentifier);
 
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : permissions.entrySet()) {
-            setPermission(newCodebase, entry.getKey(), entry.getValue());
+            setPermission(newIdentifier, entry.getKey(), entry.getValue());
         }
 
-        for (final CustomPermission permission : customPermissions) {
-            addCustomPermission(newCodebase, permission);
+        for (final PolicyParser.PermissionEntry permission : customPermissions) {
+            addCustomPermission(newIdentifier, permission);
         }
-
-        updateCheckboxes(newCodebase);
+        updateCheckboxes(newIdentifier);
     }
 
     /**
-     * Copy a codebase to the system clipboard, preserving its permissions
-     * @param codebase the codebase to copy
+     * Copy an entry to the system clipboard
+     * @param identifier the identifier to copy
      */
-    public void copyCodebase(final String codebase) {
-        if (!getCodebases().contains(codebase)) {
+    public void copyEntry(final PolicyIdentifier identifier) {
+        if (!policyEditorController.getIdentifiers().contains(identifier)) {
             return;
         }
-        policyEditorController.copyCodebaseToClipboard(codebase);
+        policyEditorController.copyPolicyEntryToClipboard(identifier);
     }
 
     /**
-     * Paste a codebase entry from the system clipboard with a new codebase name
+     * Paste a grant entry from the system clipboard with a new identifier
      */
-    public void pasteCodebase(final String newCodebase) throws UnsupportedFlavorException, InvalidPolicyException, IOException {
-        final Map<PolicyEditorPermissions, Boolean> permissions = policyEditorController.getPermissionsFromClipboard();
-        final Set<CustomPermission> customPermissions = policyEditorController.getCustomPermissionsFromClipboard();
-        addNewCodebase(newCodebase);
-        for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : permissions.entrySet()) {
-            policyEditorController.setPermission(newCodebase, entry.getKey(), entry.getValue());
-        }
-        policyEditorController.addCustomPermissions(newCodebase, customPermissions);
+    public void pasteEntry(final PolicyIdentifier identifier) throws UnsupportedFlavorException, PolicyParser.ParsingException, IOException {
+        addNewEntry(identifier);
+        final PolicyEntry policyEntry = PolicyEditorController.getPolicyEntryFromClipboard();
+        final PolicyEntry newEntry = new PolicyEntry.Builder()
+                .signedBy(identifier.getSignedBy())
+                .principals(identifier.getPrincipals())
+                .codebase(identifier.getCodebase())
+                .permissions(policyEntry.getPermissions())
+                .customPermissions(policyEntry.getCustomPermissions())
+                .build();
+        policyEditorController.addPolicyEntry(newEntry);
         setChangesMade(true);
-        updateCheckboxes(newCodebase);
-    }
-
-    public Set<String> getCodebases() {
-        return policyEditorController.getCodebases();
+        updateCheckboxes(identifier);
     }
 
-    public void setPermission(final String codebase, final PolicyEditorPermissions permission, final boolean state) {
-        policyEditorController.setPermission(codebase, permission, state);
+    Set<String> getCodebases() {
+        final Set<String> codebases = new HashSet<>();
+        for (final PolicyIdentifier identifier : policyEditorController.getIdentifiers()) {
+            if (isCodeBaseIdentifier(identifier)) {
+                codebases.add(identifier.getCodebase());
+            }
+        }
+        return codebases;
     }
 
-    public Map<PolicyEditorPermissions, Boolean> getPermissions(final String codebase) {
-        return policyEditorController.getPermissions(codebase);
+    static boolean isCodeBaseIdentifier(final PolicyIdentifier identifier) {
+        return (identifier.getSignedBy() == null || identifier.getSignedBy().isEmpty())
+                && (identifier.getPrincipals() == null || identifier.getPrincipals().isEmpty())
+                && identifier.getCodebase() != null;
     }
 
-    public void addCustomPermission(final String codebase, final CustomPermission permission) {
-        policyEditorController.addCustomPermission(codebase, permission);
+    public void setPermission(final PolicyIdentifier identifier, final PolicyEditorPermissions permission, final boolean state) {
+        policyEditorController.setPermission(identifier, permission, state);
+    }
+
+    public Map<PolicyEditorPermissions, Boolean> getPermissions(final PolicyIdentifier identifier) {
+        return policyEditorController.getPermissions(identifier);
     }
 
-    public Collection<CustomPermission> getCustomPermissions(final String codebase) {
-        return policyEditorController.getCustomPermissions(codebase);
+    public void addCustomPermission(final PolicyIdentifier identifier, final PolicyParser.PermissionEntry permission) {
+        policyEditorController.addCustomPermission(identifier, permission);
     }
 
-    public void clearCustomPermissions(final String codebase) {
-        policyEditorController.clearCustomCodebase(codebase);
+    public Collection<PolicyParser.PermissionEntry> getCustomPermissions(final PolicyIdentifier identifier) {
+        return policyEditorController.getCustomPermissions(identifier);
+    }
+
+    public void clearCustomPermissions(final PolicyIdentifier identifier) {
+        policyEditorController.clearCustomIdentifier(identifier);
     }
 
     private void invokeRunnableOrEnqueueLater(final Runnable runnable) {
@@ -962,35 +1059,33 @@
     }
 
     /**
-     * Update the checkboxes to show the permissions granted to the specified codebase
-     * @param codebase whose permissions to display
+     * Update the checkboxes to show the permissions granted to the specified identifier
+     * @param identifier whose permissions to display
      */
-    private void updateCheckboxes(final String codebase) {
+    private void updateCheckboxes(final PolicyIdentifier identifier) {
         try {
             invokeRunnableOrEnqueueAndWait(new Runnable() {
                 @Override
                 public void run() {
-                    updateCheckboxesImpl(codebase);
+                    updateCheckboxesImpl(identifier);
                 }
             });
-        } catch (InterruptedException ex) {
-            OutputController.getLogger().log(ex);
-        } catch (InvocationTargetException ex) {
+        } catch (final InterruptedException | InvocationTargetException ex) {
             OutputController.getLogger().log(ex);
         }
     }
 
-    private void updateCheckboxesImpl(final String codebase) {
-        if (!getCodebases().contains(codebase)) {
+    private void updateCheckboxesImpl(final PolicyIdentifier identifier) {
+        if (!listModel.contains(identifier)) {
             return;
         }
-        final Map<PolicyEditorPermissions, Boolean> map = policyEditorController.getCopyOfPermissions().get(codebase);
+        final Map<PolicyEditorPermissions, Boolean> map = policyEditorController.getCopyOfPermissions().get(identifier);
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
             final JCheckBox box = checkboxMap.get(perm);
             for (final ActionListener l : box.getActionListeners()) {
                 box.removeActionListener(l);
             }
-            final boolean state = policyEditorController.getPermission(codebase, perm);
+            final boolean state = policyEditorController.getPermission(identifier, perm);
             for (final JCheckBoxWithGroup jg : groupBoxList) {
                 jg.setState(map);
             }
@@ -999,7 +1094,7 @@
                 @Override
                 public void actionPerformed(final ActionEvent e) {
                     setChangesMade(true);
-                    policyEditorController.setPermission(codebase, perm, box.isSelected());
+                    policyEditorController.setPermission(identifier, perm, box.isSelected());
                     for (JCheckBoxWithGroup jg : groupBoxList) {
                         jg.setState(map);
                     }
@@ -1062,6 +1157,8 @@
         setMenuItemAccelerator(saveAsItem, R("PESaveAsMenuItemAccelerator"));
         saveAsItem.addActionListener(editor.saveAsButtonAction);
         fileMenu.add(saveAsItem);
+
+        fileMenu.addSeparator();
         
         final JMenuItem exitItem = new JMenuItem(R("PEExitMenuItem"));
         setButtonMnemonic(exitItem, R("PEExitMenuItemMnemonic"));
@@ -1070,41 +1167,59 @@
         fileMenu.add(exitItem);
         menuBar.add(fileMenu);
 
-        final JMenu codebaseMenu = new JMenu(R("PECodebaseMenu"));
-        setButtonMnemonic(codebaseMenu, R("PECodebaseMenuMnemonic"));
+        final JMenu entryMenu = new JMenu(R("PEEntryMenu"));
+        setButtonMnemonic(entryMenu, R("PEEntryMenuMnemonic"));
+
+        final JMenuItem addEntryItem = new JMenuItem(R("PEAddEntryItem"));
+        setButtonMnemonic(addEntryItem, R("PEAddEntryItemMnemonic"));
+        setMenuItemAccelerator(addEntryItem, R("PEAddEntryItemAccelerator"));
+        addEntryItem.addActionListener(editor.addEntryButtonAction);
+        entryMenu.add(addEntryItem);
 
-        final JMenuItem addNewCodebaseItem = new JMenuItem(R("PEAddCodebaseItem"));
-        setButtonMnemonic(addNewCodebaseItem, R("PEAddCodebaseItemMnemonic"));
-        setMenuItemAccelerator(addNewCodebaseItem, R("PEAddCodebaseItemAccelerator"));
-        addNewCodebaseItem.addActionListener(editor.addCodebaseButtonAction);
-        codebaseMenu.add(addNewCodebaseItem);
+        final JMenuItem removeEntryItem = new JMenuItem(R("PERemoveEntryItem"));
+        setButtonMnemonic(removeEntryItem, R("PERemoveEntryItemMnemonic"));
+        setMenuItemAccelerator(removeEntryItem, R("PERemoveEntryItemAccelerator"));
+        removeEntryItem.addActionListener(editor.removeEntryButtonAction);
+        entryMenu.add(removeEntryItem);
+
+        entryMenu.addSeparator();
 
-        final JMenuItem removeCodebaseItem = new JMenuItem(R("PERemoveCodebaseItem"));
-        setButtonMnemonic(removeCodebaseItem, R("PERemoveCodebaseItemMnemonic"));
-        setMenuItemAccelerator(removeCodebaseItem, R("PERemoveCodebaseItemAccelerator"));
-        removeCodebaseItem.addActionListener(editor.removeCodebaseButtonAction);
-        codebaseMenu.add(removeCodebaseItem);
+        final JMenu modifySubmenuItem = new JMenu(R("PEModifySubmenuItem"));
+        setButtonMnemonic(modifySubmenuItem, R("PEModifySubmenuItemMnemonic"));
 
-        codebaseMenu.addSeparator();
+        final JMenuItem modifyCodebaseItem = new JMenuItem(R("PEModifyCodebaseItem"));
+        setButtonMnemonic(modifyCodebaseItem, R("PEModifyEntryCodebaseItemMnemonic"));
+        setMenuItemAccelerator(modifyCodebaseItem, R("PEModifyEntryCodebaseItemAccelerator"));
+        modifyCodebaseItem.addActionListener(editor.modifyCodebaseButtonAction);
+        modifySubmenuItem.add(modifyCodebaseItem);
 
-        final JMenuItem renameCodebaseItem = new JMenuItem(R("PERenameCodebaseItem"));
-        setButtonMnemonic(renameCodebaseItem, R("PERenameCodebaseItemMnemonic"));
-        setMenuItemAccelerator(renameCodebaseItem, R("PERenameCodebaseItemAccelerator"));
-        renameCodebaseItem.addActionListener(editor.renameCodebaseButtonAction);
-        codebaseMenu.add(renameCodebaseItem);
+        final JMenuItem modifyPrincipalsItem = new JMenuItem(R("PEModifyPrincipalsItem"));
+        setButtonMnemonic(modifyPrincipalsItem, R("PEModifyEntryPrincipalsItemMnemonic"));
+        setMenuItemAccelerator(modifyPrincipalsItem, R("PEModifyEntryPrincipalsItemAccelerator"));
+        modifyPrincipalsItem.addActionListener(editor.modifyPrincipalsButtonAction);
+        modifySubmenuItem.add(modifyPrincipalsItem);
+
+        final JMenuItem modifySignedByItem = new JMenuItem(R("PEModifySignedByItem"));
+        setButtonMnemonic(modifySignedByItem, R("PEModifyEntrySignedByItemMnemonic"));
+        setMenuItemAccelerator(modifySignedByItem, R("PEModifyEntrySignedByItemAccelerator"));
+        modifySignedByItem.addActionListener(editor.modifySignedByButtonAction);
+        modifySubmenuItem.add(modifySignedByItem);
 
-        final JMenuItem copyCodebaseItem = new JMenuItem(R("PECopyCodebaseItem"));
-        setButtonMnemonic(copyCodebaseItem, R("PECopyCodebaseItemMnemonic"));
-        setMenuItemAccelerator(copyCodebaseItem, R("PECopyCodebaseItemAccelerator"));
-        copyCodebaseItem.addActionListener(editor.copyCodebaseButtonAction);
-        codebaseMenu.add(copyCodebaseItem);
-        menuBar.add(codebaseMenu);
+        entryMenu.add(modifySubmenuItem);
+        entryMenu.addSeparator();
 
-        final JMenuItem pasteCodebaseItem = new JMenuItem(R("PEPasteCodebaseItem"));
-        setButtonMnemonic(pasteCodebaseItem, R("PEPasteCodebaseItemMnemonic"));
-        setMenuItemAccelerator(pasteCodebaseItem, R("PEPasteCodebaseItemAccelerator"));
-        pasteCodebaseItem.addActionListener(editor.pasteCodebaseButtonAction);
-        codebaseMenu.add(pasteCodebaseItem);
+        final JMenuItem copyEntryItem = new JMenuItem(R("PECopyEntryItem"));
+        setButtonMnemonic(copyEntryItem, R("PECopyEntryItemMnemonic"));
+        setMenuItemAccelerator(copyEntryItem, R("PECopyEntryItemAccelerator"));
+        copyEntryItem.addActionListener(editor.copyEntryButtonAction);
+        entryMenu.add(copyEntryItem);
+        menuBar.add(entryMenu);
+
+        final JMenuItem pasteEntryItem = new JMenuItem(R("PEPasteEntryItem"));
+        setButtonMnemonic(pasteEntryItem, R("PEPasteEntryItemMnemonic"));
+        setMenuItemAccelerator(pasteEntryItem, R("PEPasteEntryItemAccelerator"));
+        pasteEntryItem.addActionListener(editor.pasteEntryButtonAction);
+        entryMenu.add(pasteEntryItem);
 
         final JMenu viewMenu = new JMenu(R("PEViewMenu"));
         setButtonMnemonic(viewMenu, R("PEViewMenuMnemonic"));
@@ -1146,21 +1261,21 @@
         final Action listCopyOverrideAction = new AbstractAction() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                editor.copyCodebaseButtonAction.actionPerformed(e);
+                editor.copyEntryButtonAction.actionPerformed(e);
             }
         };
 
         final Action listPasteOverrideAction = new AbstractAction() {
             @Override
             public void actionPerformed(final ActionEvent e) {
-                editor.pasteCodebaseButtonAction.actionPerformed(e);
+                editor.pasteEntryButtonAction.actionPerformed(e);
             }
         };
 
-        listInputMap.put(copyCodebaseItem.getAccelerator(), "CopyCodebaseOverride");
-        listActionMap.put("CopyCodebaseOverride", listCopyOverrideAction);
-        listInputMap.put(pasteCodebaseItem.getAccelerator(), "PasteCodebaseOverride");
-        listActionMap.put("PasteCodebaseOverride", listPasteOverrideAction);
+        listInputMap.put(copyEntryItem.getAccelerator(), "CopyEntryOverride");
+        listActionMap.put("CopyEntryOverride", listCopyOverrideAction);
+        listInputMap.put(pasteEntryItem.getAccelerator(), "PasteEntryOverride");
+        listActionMap.put("PasteEntryOverride", listPasteOverrideAction);
 
         return menuBar;
     }
@@ -1199,17 +1314,17 @@
                 checkboxConstraints.gridy++;
             }
         }
-        //add groups
+        // add groups
         for (final PolicyEditorPermissions.Group g : PolicyEditorPermissions.Group.values()) {
-            //no metter what, put group title on new line
+            // no matter what, put group title on new line
             checkboxConstraints.gridy++;
-            //all groups are in second column
+            // all groups are in second column
             checkboxConstraints.gridx = 2;
             final JCheckBoxWithGroup groupCh = new JCheckBoxWithGroup(g);
             groupBoxList.add(groupCh);
             final JPanel groupPanel = new JPanel(new GridBagLayout());
             groupPanel.setBorder(new LineBorder(Color.black));
-            groupCh.setToolTipText(R("PEGrightClick"));
+            groupCh.setToolTipText(R("PEGRightClick"));
             groupCh.addMouseListener(new MouseAdapter() {
                 @Override
                 public void mouseClicked(final MouseEvent e) {
@@ -1229,8 +1344,8 @@
             groupCh.addActionListener(new ActionListener() {
                 @Override
                 public void actionPerformed(final ActionEvent e) {
-                    final String codebase = getSelectedCodebase();
-                    if (codebase == null) {
+                    final PolicyIdentifier identifier = getSelectedPolicyIdentifier();
+                    if (identifier == null) {
                         return;
                     }
                     List<ActionListener> backup = new LinkedList<>();
@@ -1239,10 +1354,10 @@
                         groupCh.removeActionListener(l);
                     }
                     for (final PolicyEditorPermissions p : groupCh.getGroup().getPermissions()) {
-                        policyEditorController.setPermission(codebase, p, groupCh.isSelected());
+                        policyEditorController.setPermission(identifier, p, groupCh.isSelected());
                     }
                     setChangesMade(true);
-                    updateCheckboxes(codebase);
+                    updateCheckboxes(identifier);
                     for (final ActionListener al : backup) {
                         groupCh.addActionListener(al);
                     }
@@ -1273,17 +1388,17 @@
                 }
             }
             groupPanel.setVisible(false);
-            //reset
+            // reset
             checkboxConstraints.gridwidth = 1;
         }
 
-        final JLabel codebaseListLabel = new JLabel(R("PECodebaseLabel"));
-        codebaseListLabel.setBorder(new EmptyBorder(2, 2, 2, 2));
+        final JLabel entriesListLabel = new JLabel(R("PEEntriesLabel"));
+        entriesListLabel.setBorder(new EmptyBorder(2, 2, 2, 2));
         final GridBagConstraints listLabelConstraints = new GridBagConstraints();
         listLabelConstraints.fill = GridBagConstraints.HORIZONTAL;
         listLabelConstraints.gridx = 0;
         listLabelConstraints.gridy = 0;
-        add(codebaseListLabel, listLabelConstraints);
+        add(entriesListLabel, listLabelConstraints);
 
         list.addListSelectionListener(new ListSelectionListener() {
             @Override
@@ -1291,11 +1406,7 @@
                 if (e.getValueIsAdjusting()) {
                     return; // ignore first click, act on release
                 }
-                final String codebase = getSelectedCodebase();
-                if (codebase == null) {
-                    return;
-                }
-                updateCheckboxes(codebase);
+                updateCheckboxes(getSelectedPolicyIdentifier());
             }
         });
         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -1313,25 +1424,25 @@
         listConstraints.gridy = 1;
         add(scrollPane, listConstraints);
 
-        final GridBagConstraints addCodebaseButtonConstraints = new GridBagConstraints();
-        addCodebaseButtonConstraints.fill = GridBagConstraints.HORIZONTAL;
-        addCodebaseButtonConstraints.gridx = 0;
-        addCodebaseButtonConstraints.gridy = listConstraints.gridy + listConstraints.gridheight + 1;
-        setButtonMnemonic(addCodebaseButton, R("PEAddCodebaseMnemonic"));
-        add(addCodebaseButton, addCodebaseButtonConstraints);
+        final GridBagConstraints addEntryButtonConstraints = new GridBagConstraints();
+        addEntryButtonConstraints.fill = GridBagConstraints.HORIZONTAL;
+        addEntryButtonConstraints.gridx = 0;
+        addEntryButtonConstraints.gridy = listConstraints.gridy + listConstraints.gridheight + 1;
+        setButtonMnemonic(addEntryButton, R("PEAddEntryMnemonic"));
+        add(addEntryButton, addEntryButtonConstraints);
 
-        final GridBagConstraints removeCodebaseButtonConstraints = new GridBagConstraints();
-        removeCodebaseButtonConstraints.fill = GridBagConstraints.HORIZONTAL;
-        removeCodebaseButtonConstraints.gridx = addCodebaseButtonConstraints.gridx + 1;
-        removeCodebaseButtonConstraints.gridy = addCodebaseButtonConstraints.gridy;
-        setButtonMnemonic(removeCodebaseButton, R("PERemoveCodebaseMnemonic"));
-        removeCodebaseButton.setPreferredSize(addCodebaseButton.getPreferredSize());
-        add(removeCodebaseButton, removeCodebaseButtonConstraints);
+        final GridBagConstraints removeEntryButtonConstraints = new GridBagConstraints();
+        removeEntryButtonConstraints.fill = GridBagConstraints.HORIZONTAL;
+        removeEntryButtonConstraints.gridx = addEntryButtonConstraints.gridx + 1;
+        removeEntryButtonConstraints.gridy = addEntryButtonConstraints.gridy;
+        setButtonMnemonic(removeEntryButton, R("PERemoveEntryMnemonic"));
+        removeEntryButton.setPreferredSize(addEntryButton.getPreferredSize());
+        add(removeEntryButton, removeEntryButtonConstraints);
 
         final GridBagConstraints okButtonConstraints = new GridBagConstraints();
         okButtonConstraints.fill = GridBagConstraints.HORIZONTAL;
-        okButtonConstraints.gridx = removeCodebaseButtonConstraints.gridx + 2;
-        okButtonConstraints.gridy = removeCodebaseButtonConstraints.gridy;
+        okButtonConstraints.gridx = removeEntryButtonConstraints.gridx + 2;
+        okButtonConstraints.gridy = removeEntryButtonConstraints.gridy;
         add(okButton, okButtonConstraints);
 
         final GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
@@ -1353,28 +1464,57 @@
         });
     }
 
-    private void resetCodebases() {
+    private void resetEntries() {
         listModel.clear();
-        policyEditorController.clearPermissions();
-        policyEditorController.clearCustomPermissions();
+        policyEditorController.clear();
     }
 
     /**
      * @return whether this PolicyEditor is currently opening or saving a policy file to disk
      */
     public boolean isPerformingIO() {
-        return policyEditorController.performingIO();
+        return policyEditorController.isPerformingIO();
     }
 
-    private void openAndParsePolicyFile() {
-        resetCodebases();
+    public void openPolicyFileSynchronously() {
+        if (getFile() == null) {
+            return;
+        }
+        resetEntries();
+        final OpenFileResult ofr = FileUtils.testFilePermissions(getFile());
+        if (ofr == OpenFileResult.FAILURE || ofr == OpenFileResult.NOT_FILE) {
+            addDefaultAllAppletsIdentifier();
+            OutputController.getLogger().log(R("PECouldNotOpen"));
+        }
+        if (ofr == OpenFileResult.CANT_WRITE) {
+            OutputController.getLogger().log(R("RFileReadOnly"));
+        }
+
         try {
-            policyEditorController.getFile().createNewFile();
-        } catch (final IOException e) {
+            policyEditorController.openAndParsePolicyFile();
+        } catch (IOException | PolicyParser.ParsingException e) {
             OutputController.getLogger().log(e);
+            return;
         }
-        final OpenFileResult ofr = FileUtils.testFilePermissions(policyEditorController.getFile());
+
+        for (final PolicyIdentifier identifier : policyEditorController.getIdentifiers()) {
+            if (!listModel.contains(identifier)) {
+                listModel.addElement(identifier);
+            }
+        }
+        addDefaultAllAppletsIdentifier();
+        updateCheckboxes(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
+        setChangesMade(false);
+    }
+
+    public void openAndParsePolicyFile() {
+        if (getFile() == null) {
+            return;
+        }
+        resetEntries();
+        final OpenFileResult ofr = FileUtils.testFilePermissions(getFile());
         if (ofr == OpenFileResult.FAILURE || ofr == OpenFileResult.NOT_FILE) {
+            addDefaultAllAppletsIdentifier();
             FileUtils.showCouldNotOpenFilepathDialog(PolicyEditor.this, policyEditorController.getFile().getPath());
             return;
         }
@@ -1382,7 +1522,6 @@
             FileUtils.showReadOnlyDialog(PolicyEditor.this);
         }
 
-
         final Window parentWindow = SwingUtilities.getWindowAncestor(this);
         final JDialog progressIndicator = new IndeterminateProgressDialog(parentWindow, "Loading...");
         final SwingWorker<Void, Void> openPolicyFileWorker = new SwingWorker<Void, Void>() {
@@ -1402,7 +1541,7 @@
                 } catch (final FileNotFoundException fnfe) {
                     OutputController.getLogger().log(fnfe);
                     FileUtils.showCouldNotOpenDialog(PolicyEditor.this, R("PECouldNotOpen"));
-                } catch (final IOException | InvalidPolicyException e) {
+                } catch (final IOException | PolicyParser.ParsingException e) {
                     OutputController.getLogger().log(e);
                     OutputController.getLogger().log(OutputController.Level.ERROR_ALL, R("RCantOpenFile", policyEditorController.getFile().getPath()));
                     FileUtils.showCouldNotOpenDialog(PolicyEditor.this, R("PECouldNotOpen"));
@@ -1412,18 +1551,13 @@
 
             @Override
             public void done() {
-                for (final String codebase : policyEditorController.getCodebases()) {
-                    final String model;
-                    if (codebase.isEmpty()) {
-                        model = R("PEGlobalSettings");
-                    } else {
-                        model = codebase;
-                    }
-                    if (!listModel.contains(model)) {
-                        listModel.addElement(model);
+                for (final PolicyIdentifier identifier : policyEditorController.getIdentifiers()) {
+                    if (!listModel.contains(identifier)) {
+                        listModel.addElement(identifier);
                     }
                 }
-                addNewCodebase("");
+                addDefaultAllAppletsIdentifier();
+                updateCheckboxes(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
                 progressIndicator.setVisible(false);
                 progressIndicator.dispose();
                 setChangesMade(false);
@@ -1522,11 +1656,11 @@
         });
     }
 
-    private void showInvalidPolicyExceptionDialog(final String codebase) {
+    private void showInvalidPolicyExceptionDialog(final PolicyIdentifier identifier) {
         invokeRunnableOrEnqueueLater(new Runnable() {
             @Override
             public void run() {
-                JOptionPane.showMessageDialog(parentPolicyEditor.get(), R("PEInvalidPolicy", codebase), R("Error"), JOptionPane.ERROR_MESSAGE);
+                JOptionPane.showMessageDialog(parentPolicyEditor.get(), R("PEInvalidPolicy", identifier.toString()), R("Error"), JOptionPane.ERROR_MESSAGE);
             }
         });
     }
@@ -1546,7 +1680,6 @@
      * @return The user's choice (Yes/No/Cancel - see JOptionPane constants). 
      * "Cancel" if the file hasn't changed but the user has made modifications
      * to the settings. "No" otherwise
-     * @throws IOException if the file cannot be read
      */
     private int checkPolicyChangesWithDialog() {
         boolean changed;
@@ -1591,9 +1724,6 @@
      * @param args "-file $FILENAME" and/or "-codebase $CODEBASE" are accepted flag/value pairs.
      * -file specifies a file path to be opened by the editor. If none is provided, the default
      * policy file location for the user is opened.
-     * -codebase specifies (a) codebase(s) to start the editor with. If the entry already exists,
-     * it will be selected. If it does not exist, it will be created, then selected. Multiple
-     * codebases can be used, separated by spaces.
      * -help will print a help message and immediately return (no editor instance opens)
      */
     public static void main(final String[] args) {
@@ -1629,20 +1759,24 @@
             @Override
             public void run() {
                 String filepath = optionParser.getParam(OptionsDefinitions.OPTIONS.FILE);
-                if (filepath.isEmpty()) {
-                    if (optionParser.getMainArgs().size() == 0) {
-                        filepath = null;
-                    } else {
-                        // maybe the user just forgot the -file flag, so try to open anyway
-                        filepath = optionParser.getMainArg();
-                    }
+                if (filepath == null || filepath.isEmpty() || filepath.trim().isEmpty()) {
+                    // maybe the user just forgot the -file flag, so try to open anyway
+                    filepath = optionParser.getMainArg();
+                }
+                if (filepath == null || filepath.isEmpty() || filepath.trim().isEmpty()) {
+                    filepath = null;
                 }
                 final PolicyEditorWindow frame = getPolicyEditorFrame(filepath);
+                final String codebase = optionParser.getParam(OptionsDefinitions.OPTIONS.CODEBASE);
+                final String signedBy = optionParser.getParam(OptionsDefinitions.OPTIONS.SIGNEDBY);
+                final List<String> rawPrincipals = optionParser.getParams(OptionsDefinitions.OPTIONS.PRINCIPALS);
+                final Set<PolicyParser.PrincipalEntry> principals = new HashSet<>();
+                for (int i = 0; i < rawPrincipals.size(); i+= 2) {
+                    principals.add(new PolicyParser.PrincipalEntry(rawPrincipals.get(i), rawPrincipals.get(i + 1)));
+                }
+                frame.getPolicyEditor().openPolicyFileSynchronously();
+                frame.getPolicyEditor().addNewEntry(new PolicyIdentifier(signedBy, principals, codebase));
                 frame.asWindow().setVisible(true);
-                final List<String> codebases = optionParser.getParams(OptionsDefinitions.OPTIONS.CODEBASE);
-                for (final String url : codebases) {
-                    frame.getPolicyEditor().addNewCodebase(url);
-                }
             }
         });
     }
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorController.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorController.java	Thu Jul 30 10:57:21 2015 -0400
@@ -37,21 +37,22 @@
 package net.sourceforge.jnlp.security.policyeditor;
 
 import java.awt.Toolkit;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.UnsupportedFlavorException;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
 import net.sourceforge.jnlp.util.logging.OutputController;
+import sun.security.provider.PolicyParser;
+
+import static net.sourceforge.jnlp.security.policyeditor.PolicyEntry.POLICY_ENTRY_DATA_FLAVOR;
 
 public class PolicyEditorController {
 
@@ -67,7 +68,7 @@
         changesMade = b;
     }
 
-    boolean performingIO() {
+    boolean isPerformingIO() {
         return performingIO;
     }
 
@@ -76,53 +77,58 @@
     }
 
     public void setFile(final File file) {
-        setChangesMade(true);
-        policyFile.setFile(file);
+        boolean changedFile = policyFile.setFile(file);
+        setChangesMade(changedFile);
     }
 
     public File getFile() {
         return policyFile.getFile();
     }
 
-    public boolean fileHasChanged() throws FileNotFoundException, IOException {
+    public boolean fileHasChanged() throws IOException {
         return policyFile.hasChanged();
     }
 
-    public boolean addCodebase(final String codebase) {
-        final boolean existed = policyFile.addCodebase(codebase);
+    public boolean addIdentifier(final PolicyIdentifier identifier) {
+        final boolean existed = policyFile.addIdentifier(identifier);
         if (!existed) {
             setChangesMade(true);
         }
         return existed;
     }
 
-    public void removeCodebase(final String codebase) {
+    public void removeIdentifier(final PolicyIdentifier identifier) {
         setChangesMade(true);
-        policyFile.removeCodebase(codebase);
+        policyFile.removeIdentifier(identifier);
     }
 
-    public Set<String> getCodebases() {
-        return new HashSet<>(policyFile.getCodebases());
+    public Set<PolicyIdentifier> getIdentifiers() {
+        return new HashSet<>(policyFile.getIdentifiers());
     }
 
-    public Map<String, Map<PolicyEditorPermissions, Boolean>> getCopyOfPermissions() {
+    public Map<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>> getCopyOfPermissions() {
         return policyFile.getCopyOfPermissions();
     }
 
-    public void setPermission(final String codebase, final PolicyEditorPermissions permission, final boolean state) {
-        if (getPermission(codebase, permission) != state) {
+    public void setPermission(final PolicyIdentifier identifier, final PolicyEditorPermissions permission, final boolean state) {
+        if (getPermission(identifier, permission) != state) {
             setChangesMade(true);
         }
-        policyFile.setPermission(codebase, permission, state);
+        policyFile.setPermission(identifier, permission, state);
     }
 
-    public boolean getPermission(final String codebase, final PolicyEditorPermissions permission) {
-        return policyFile.getPermission(codebase, permission);
+    public boolean getPermission(final PolicyIdentifier identifier, final PolicyEditorPermissions permission) {
+        return policyFile.getPermission(identifier, permission);
     }
 
-    public Map<PolicyEditorPermissions, Boolean> getPermissions(final String codebase) {
-        policyFile.addCodebase(codebase);
-        return new HashMap<>(policyFile.getCopyOfPermissions().get(codebase));
+    public Map<PolicyEditorPermissions, Boolean> getPermissions(final PolicyIdentifier identifier) {
+        policyFile.addIdentifier(identifier);
+        return new HashMap<>(policyFile.getCopyOfPermissions().get(identifier));
+    }
+
+    public void clear() {
+        setChangesMade(true);
+        policyFile.clearPermissions();
     }
 
     public void clearPermissions() {
@@ -130,32 +136,32 @@
         policyFile.clearPermissions();
     }
 
-    public void addCustomPermissions(final String codebase, final Collection<CustomPermission> permissions) {
-        if (!policyFile.getCopyOfCustomPermissions().equals(permissions)) {
+    public void addCustomPermissions(final PolicyIdentifier identifier, final Collection<PolicyParser.PermissionEntry> permissions) {
+        if (!policyFile.getCopyOfCustomPermissions().containsKey(identifier) || !policyFile.getCopyOfCustomPermissions().get(identifier).equals(permissions)) {
             setChangesMade(true);
         }
-        policyFile.addCustomPermissions(codebase, permissions);
+        policyFile.addCustomPermissions(identifier, permissions);
     }
 
-    public void addCustomPermission(final String codebase, final CustomPermission permission) {
-        final Map<String, Set<CustomPermission>> customs = policyFile.getCopyOfCustomPermissions();
-        if (customs == null || !customs.containsKey(codebase) || (customs.containsKey(codebase) && !customs.get(codebase).contains(permission))) {
+    public void addCustomPermission(final PolicyIdentifier identifier, final PolicyParser.PermissionEntry permission) {
+        final Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> customs = policyFile.getCopyOfCustomPermissions();
+        if (customs == null || !customs.containsKey(identifier) || (customs.containsKey(identifier) && !customs.get(identifier).contains(permission))) {
             setChangesMade(true);
         }
-        addCustomPermissions(codebase, Arrays.asList(permission));
+        addCustomPermissions(identifier, Collections.singletonList(permission));
     }
 
-    public Set<CustomPermission> getCustomPermissions(final String codebase) {
-        policyFile.addCodebase(codebase);
-        return new HashSet<>(policyFile.getCopyOfCustomPermissions().get(codebase));
+    public Set<PolicyParser.PermissionEntry> getCustomPermissions(final PolicyIdentifier identifier) {
+        policyFile.addIdentifier(identifier);
+        return new HashSet<>(policyFile.getCopyOfCustomPermissions().get(identifier));
     }
 
     public void addPolicyEntry(final PolicyEntry policyEntry) {
-        addCodebase(policyEntry.getCodebase());
+        addIdentifier(policyEntry.getPolicyIdentifier());
         for (final PolicyEditorPermissions permission : policyEntry.getPermissions()) {
-            setPermission(policyEntry.getCodebase(), permission, true);
+            setPermission(policyEntry.getPolicyIdentifier(), permission, true);
         }
-        addCustomPermissions(policyEntry.getCodebase(), policyEntry.getCustomPermissions());
+        addCustomPermissions(policyEntry.getPolicyIdentifier(), policyEntry.getCustomPermissions());
     }
 
     public void clearCustomPermissions() {
@@ -163,18 +169,12 @@
         policyFile.clearCustomPermissions();
     }
 
-    public void clearCustomCodebase(final String codebase) {
+    public void clearCustomIdentifier(final PolicyIdentifier identifier) {
         setChangesMade(true);
-        policyFile.clearCustomCodebase(codebase);
+        policyFile.clearCustomIdentifier(identifier);
     }
 
-    public void openAndParsePolicyFile() throws IOException, InvalidPolicyException {
-        try {
-            policyFile.getFile().createNewFile();
-        } catch (final IOException e) {
-            OutputController.getLogger().log(e);
-        }
-
+    public void openAndParsePolicyFile() throws IOException, PolicyParser.ParsingException {
         setPerformingIO(true);
         policyFile.openAndParsePolicyFile();
 
@@ -182,7 +182,7 @@
         setPerformingIO(false);
     }
 
-    public void savePolicyFile() throws FileNotFoundException, IOException {
+    public void savePolicyFile() throws IOException {
         setPerformingIO(true);
         policyFile.savePolicyFile();
 
@@ -190,45 +190,29 @@
         setPerformingIO(false);
     }
 
-    public void copyCodebaseToClipboard(final String codebase) {
-        final Map<PolicyEditorPermissions, Boolean> standardPermissions = policyFile.getCopyOfPermissions().get(codebase);
-        final Set<CustomPermission> customPermissions = policyFile.getCopyOfCustomPermissions().get(codebase);
+    public void copyPolicyEntryToClipboard(final PolicyIdentifier identifier) {
+        final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+        final PolicyEntry policyEntry = getPolicyEntry(identifier);
+        clipboard.setContents(policyEntry, null);
+    }
 
-        final Set<PolicyEditorPermissions> enabledPermissions = new HashSet<>();
-        for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : standardPermissions.entrySet()) {
+    public PolicyEntry getPolicyEntry(final PolicyIdentifier identifier) {
+        final Collection<PolicyEditorPermissions> enabledPermissions = new HashSet<>();
+        for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : getPermissions(identifier).entrySet()) {
             if (entry.getValue()) {
                 enabledPermissions.add(entry.getKey());
             }
         }
-        final PolicyEntry entry = new PolicyEntry(codebase, enabledPermissions, customPermissions);
-        final StringSelection clipboardSelection = new StringSelection(entry.toString());
-        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(clipboardSelection, clipboardSelection);
-    }
-
-    private static String getClipboardContentsAsString() throws IOException, UnsupportedFlavorException {
-        final Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
-        return (String) transferable.getTransferData(DataFlavor.stringFlavor);
-    }
-
-    public static PolicyEntry getPolicyEntryFromClipboard() throws IOException, UnsupportedFlavorException, InvalidPolicyException {
-        return PolicyEntry.fromString(getClipboardContentsAsString());
+        return new PolicyEntry.Builder()
+                .identifier(identifier)
+                .permissions(enabledPermissions)
+                .customPermissions(getCustomPermissions(identifier))
+                .build();
     }
 
-    public String getCodebaseFromClipboard() throws IOException, UnsupportedFlavorException, InvalidPolicyException {
-        return getPolicyEntryFromClipboard().getCodebase();
-    }
-
-    public Map<PolicyEditorPermissions, Boolean> getPermissionsFromClipboard() throws IOException, UnsupportedFlavorException, InvalidPolicyException {
-        final Map<PolicyEditorPermissions, Boolean> ret = new HashMap<>();
-        final Set<PolicyEditorPermissions> enabledPermissions = getPolicyEntryFromClipboard().getPermissions();
-        for (final PolicyEditorPermissions permission : PolicyEditorPermissions.values()) {
-            ret.put(permission, enabledPermissions.contains(permission));
-        }
-        return ret;
-    }
-
-    public Set<CustomPermission> getCustomPermissionsFromClipboard() throws IOException, UnsupportedFlavorException, InvalidPolicyException {
-        return getPolicyEntryFromClipboard().getCustomPermissions();
+    public static PolicyEntry getPolicyEntryFromClipboard() throws IOException, UnsupportedFlavorException, PolicyParser.ParsingException {
+        final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+        return (PolicyEntry) clipboard.getContents(null).getTransferData(POLICY_ENTRY_DATA_FLAVOR);
     }
 
 }
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissions.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissions.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,6 +36,9 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
+import sun.security.provider.PolicyParser;
+
+import java.io.Serializable;
 import java.util.Map;
 import javax.swing.JCheckBox;
 import static net.sourceforge.jnlp.runtime.Translator.R;
@@ -45,7 +48,7 @@
  * Defines the set of default permissions for PolicyEditor, ie the ones which are assigned
  * dedicated checkboxes
  */
-public enum PolicyEditorPermissions {
+public enum PolicyEditorPermissions implements Serializable {
 
     READ_LOCAL_FILES(R("PEReadFiles"), R("PEReadFilesDetail"),
             PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.READ),
@@ -123,7 +126,7 @@
 
         ReadFileSystem(R("PEGReadFileSystem"),  READ_LOCAL_FILES, READ_PROPERTIES, READ_SYSTEM_FILES, READ_TMP_FILES, GET_ENV),
         WriteFileSystem(R("PEGWriteFileSystem"), WRITE_LOCAL_FILES, DELETE_LOCAL_FILES, WRITE_PROPERTIES, WRITE_SYSTEM_FILES, WRITE_TMP_FILES, DELETE_TMP_FILES, EXEC_COMMANDS),
-        AccessUnownedCode(R("PEGAccesUnowenedCode"), JAVA_REFLECTION, GET_CLASSLOADER, ACCESS_CLASS_IN_PACKAGE, ACCESS_DECLARED_MEMBERS, ACCESS_THREADS, ACCESS_THREAD_GROUPS),
+        AccessUnownedCode(R("PEGAccessUnownedCode"), JAVA_REFLECTION, GET_CLASSLOADER, ACCESS_CLASS_IN_PACKAGE, ACCESS_DECLARED_MEMBERS, ACCESS_THREADS, ACCESS_THREAD_GROUPS),
         MediaAccess(R("PEGMediaAccess"), PLAY_AUDIO, RECORD_AUDIO, PRINT, CLIPBOARD);
 
         private final PolicyEditorPermissions[] permissions;
@@ -161,15 +164,15 @@
          * 0 invalid
          * - none is selected
          */
-        public int getState (final Map<PolicyEditorPermissions, Boolean> map) {
+        public int getState(final Map<PolicyEditorPermissions, Boolean> map) {
             boolean allTrue = true;
             boolean allFalse = true;
             for (final PolicyEditorPermissions pp : getPermissions()) {
                 final Boolean b = map.get(pp);
-                if (b == null){
+                if (b == null) {
                     return 0;
                 }
-                if (b.booleanValue()){
+                if (b) {
                     allFalse = false;
                 } else {
                     allTrue = false;
@@ -283,28 +286,18 @@
         return string.replaceAll("[\\[\\]\\s]", "");
     }
 
-    /**
-     * Get a PolicyEditorPermissions instance matching the input string
-     * @param string a full policy file permissions line, eg `permission java.io.FilePermission "${io.tmpdir}" "read;"`
-     * @return the PolicyEditorPermissions value matching the input String, or null if no such match is found
-     */
-    public static PolicyEditorPermissions fromString(final String string) {
-        final CustomPermission tmpPerm = CustomPermission.fromString(string);
-        if (tmpPerm == null) {
-            return null;
-        }
-
-        final PermissionType type = PermissionType.fromString(tmpPerm.type);
-        final PermissionTarget target = PermissionTarget.fromString(tmpPerm.target);
-        final PermissionActions actions = PermissionActions.fromString(tmpPerm.actions);
-
-        for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
-            final boolean sameType = perm.type.equals(type);
-            final boolean sameTarget = perm.target.equals(target);
-            final boolean sameActions = perm.actions.getActions().equals(actions.getActions());
-
-            if (sameType && sameTarget && sameActions) {
-                return perm;
+    public static PolicyEditorPermissions fromPermissionEntry(final PolicyParser.PermissionEntry permissionEntry) {
+        for (final PolicyEditorPermissions permission : values()) {
+            final String actionsString;
+            if (permission.getActions().equals(PermissionActions.NONE)) {
+                actionsString = null;
+            } else {
+                actionsString = permission.getActions().rawString();
+            }
+            final PolicyParser.PermissionEntry editorEntry =
+                    new PolicyParser.PermissionEntry(permission.getType().type, permission.getTarget().target, actionsString);
+            if (editorEntry.equals(permissionEntry)) {
+                return permission;
             }
         }
         return null;
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEntry.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyEntry.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,165 +36,109 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.EnumSet;
 import java.util.HashSet;
-import java.util.List;
+import java.util.Objects;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import net.sourceforge.jnlp.util.docprovider.formatters.formatters.PlainTextFormatter;
+import sun.security.provider.PolicyParser;
 
 /**
- * This class represents a codebase entry in a policy file. This is defined as a policy entry block
- * which begins with they keyword "grant" and ends with the delimiter "};". If the entry
- * contains a "codeBase $CODEBASE" substring after the "grant" keyword, then this information
- * is also included in this entry. Other entry "metadata" such as Principal is not defined.
- * Within a codebase entry block, lines are recognized and modelled as either PolicyEditorPermissions
- * or CustomPermissions.
+ * This class represents a "grant" entry in a policy file. This is defined as a policy entry block
+ * which begins with the keyword "grant" and ends with the delimiter "};".
  */
-public class PolicyEntry {
+public class PolicyEntry implements Serializable, Transferable {
+
+    public static class Builder {
+        private String signedBy, codebase;
+        private final Set<PolicyEditorPermissions> permissions = EnumSet.noneOf(PolicyEditorPermissions.class);
+        private final Set<PolicyParser.PermissionEntry> customPermissions = new HashSet<>();
+        private final Set<PolicyParser.PrincipalEntry> principals = new HashSet<>();
+
+        public Builder signedBy(final String signedBy) {
+            this.signedBy = signedBy;
+            return this;
+        }
+
+        public Builder principals(final Collection<PolicyParser.PrincipalEntry> principals) {
+            this.principals.addAll(principals);
+            return this;
+        }
+
+        public Builder codebase(final String codebase) {
+            this.codebase = codebase;
+            return this;
+        }
 
-    private final String codebase;
+        public Builder identifier(final PolicyIdentifier identifier) {
+            return signedBy(identifier.getSignedBy())
+                    .codebase(identifier.getCodebase())
+                    .principals(identifier.getPrincipals());
+        }
+
+        public Builder permissions(final Collection<PolicyEditorPermissions> permissions) {
+            this.permissions.addAll(permissions);
+            return this;
+        }
+
+        public Builder customPermissions(final Collection<? extends PolicyParser.PermissionEntry> customPermissions) {
+            this.customPermissions.addAll(customPermissions);
+            return this;
+        }
+
+        public PolicyEntry build() {
+            return new PolicyEntry(this);
+        }
+    }
+
+    public static final DataFlavor POLICY_ENTRY_DATA_FLAVOR = new DataFlavor(PolicyEntry.class, "PolicyEntry");
+
+    private final PolicyIdentifier policyIdentifier;
     private final Set<PolicyEditorPermissions> permissions = new HashSet<>();
-    private final Set<CustomPermission> customPermissions = new HashSet<>();
+    private final Set<PolicyParser.PermissionEntry> customPermissions = new HashSet<>();
 
-    public PolicyEntry(final String codebase, final Collection<PolicyEditorPermissions> permissions,
-            final Collection<CustomPermission> customPermissions) {
-        if (codebase == null) {
-            this.codebase = "";
-        } else {
-            this.codebase = codebase;
-        }
-        this.permissions.addAll(permissions);
+    private PolicyEntry(final Builder builder) {
+        this.policyIdentifier = new PolicyIdentifier(builder.signedBy, builder.principals, builder.codebase);
+        this.permissions.addAll(builder.permissions);
         this.permissions.remove(null);
-        this.customPermissions.addAll(customPermissions);
+        this.customPermissions.addAll(builder.customPermissions);
         this.customPermissions.remove(null);
     }
 
-    public String getCodebase() {
-        return codebase;
+    public PolicyIdentifier getPolicyIdentifier() {
+        return policyIdentifier;
     }
 
     public Set<PolicyEditorPermissions> getPermissions() {
         return permissions;
     }
 
-    public Set<CustomPermission> getCustomPermissions() {
+    public Set<PolicyParser.PermissionEntry> getCustomPermissions() {
         return customPermissions;
     }
 
-    public static PolicyEntry fromString(final String contents) throws InvalidPolicyException {
-        final List<String> lines = Arrays.asList(contents.split("\\r?\\n+"));
-        if (!validatePolicy(lines)) {
-            throw new InvalidPolicyException();
-        }
-
-        String codebase = "";
-        final Set<PolicyEditorPermissions> permissions = new HashSet<>();
-        final Set<CustomPermission> customPermissions = new HashSet<>();
-
-        boolean openBlock = false, commentBlock = false;
-        for (final String line : lines) {
-            // Matches eg `grant {` as well as `grant codeBase "http://redhat.com" {`
-            final Pattern openBlockPattern = Pattern.compile("grant\\s*\"?\\s*(?:codeBase)?\\s*\"?([^\"\\s]*)\"?\\s*\\{");
-            final Matcher openBlockMatcher = openBlockPattern.matcher(line);
-            if (openBlockMatcher.matches()) {
-                // Codebase URL
-                codebase = openBlockMatcher.group(1);
-                openBlock = true;
-                continue;
-            }
-
-            // Matches '};', the closing block delimiter, with any amount of whitespace on either side
-            boolean commentLine = false;
-            if (line.matches("\\s*\\};\\s*")) {
-                openBlock = false;
-            }
-            // Matches '/*', the start of a block comment
-            if (line.matches(".*/\\*.*")) {
-                commentBlock = true;
-            }
-            // Matches '*/', the end of a block comment, and '//', a single-line comment
-            if (line.matches(".*\\*/.*")) {
-                commentBlock = false;
-            }
-            if (line.matches(".*/\\*.*") && line.matches(".*\\*/.*")) {
-                commentLine = true;
-            }
-            if (line.matches("\\s*//.*")) {
-                commentLine = true;
-            }
-
-            if (!openBlock || commentBlock || commentLine) {
-                continue;
-            }
-
-            final PolicyEditorPermissions perm = PolicyEditorPermissions.fromString(line);
-            if (perm != null) {
-                permissions.add(perm);
-            } else {
-                final CustomPermission cPerm = CustomPermission.fromString(line.trim());
-                if (cPerm != null) {
-                    customPermissions.add(cPerm);
-                }
-            }
-        }
-        return new PolicyEntry(codebase, permissions, customPermissions);
-    }
-
-    public static boolean validatePolicy(final String content) {
-        return validatePolicy(Arrays.asList(content.split("\\r?\\n")));
-    }
-
-    public static boolean validatePolicy(final List<String> lines) {
-        int openerCount = 0, closerCount = 0;
-        for (final String line : lines) {
-            final Pattern openBlockPattern = Pattern.compile("grant\\s*\"?\\s*(?:codeBase)?\\s*\"?([^\"\\s]*)\"?\\s*\\{");
-            final Matcher openBlockMatcher = openBlockPattern.matcher(line);
-            if (openBlockMatcher.matches()) {
-                ++openerCount;
-            }
-
-            if (line.matches("\\s*\\};\\s*")) {
-                ++closerCount;
-            }
-        }
-        return (openerCount == 1) && (closerCount == 1);
+    @Override
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[] { POLICY_ENTRY_DATA_FLAVOR };
     }
 
     @Override
-    public String toString() {
-        // Empty codebase is the default "All Applets" codebase. If there are no permissions
-        // applied to it, then don't bother recording it in the policy file.
-        if (codebase.isEmpty() && permissions.isEmpty() && customPermissions.isEmpty()) {
-            return "";
-        }
-        final String newline = PlainTextFormatter.getLineSeparator();
-        final StringBuilder result = new StringBuilder();
+    public boolean isDataFlavorSupported(final DataFlavor dataFlavor) {
+        return Objects.equals(POLICY_ENTRY_DATA_FLAVOR, dataFlavor);
+    }
 
-        result.append(newline);
-        result.append("grant");
-        if (!codebase.isEmpty()) {
-            result.append(" codeBase \"");
-            result.append(codebase);
-            result.append("\"");
+    @Override
+    public Object getTransferData(final DataFlavor dataFlavor) throws UnsupportedFlavorException, IOException {
+        if (!Arrays.asList(getTransferDataFlavors()).contains(dataFlavor)) {
+            throw new UnsupportedFlavorException(dataFlavor);
         }
-        result.append(" {");
-        result.append(newline);
-        for (final PolicyEditorPermissions perm : permissions) {
-            result.append("\t");
-            result.append(perm.toPermissionString());
-            result.append(newline);
-        }
-        for (final CustomPermission customPerm : customPermissions) {
-            result.append("\t");
-            result.append(customPerm.toString().trim());
-            result.append(newline);
-        }
-        result.append("};");
-        result.append(newline);
-        return result.toString();
+        return this;
     }
 
 }
--- a/netx/net/sourceforge/jnlp/security/policyeditor/PolicyFileModel.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyFileModel.java	Thu Jul 30 10:57:21 2015 -0400
@@ -37,11 +37,10 @@
 package net.sourceforge.jnlp.security.policyeditor;
 
 import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.channels.FileLock;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -49,27 +48,28 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import net.sourceforge.jnlp.util.FileUtils;
 import net.sourceforge.jnlp.util.MD5SumWatcher;
-import net.sourceforge.jnlp.util.docprovider.formatters.formatters.PlainTextFormatter;
 import net.sourceforge.jnlp.util.logging.OutputController;
+import sun.security.provider.PolicyParser;
+
+import static net.sourceforge.jnlp.security.policyeditor.PolicyIdentifier.isDefaultPolicyIdentifier;
 
 public class PolicyFileModel {
 
-    private static final String AUTOGENERATED_NOTICE = "/* DO NOT MODIFY! AUTO-GENERATED */";
-
     private File file;
     /**
      * Maps Codebases to Maps of Permissions and whether that Permission is set or not. The Codebase keys correspond to
      * the Codebases in the list UI, and the Permission->Boolean maps correspond to the checkboxes associated with
      * each Codebase.
      */
-    public final Map<String, Map<PolicyEditorPermissions, Boolean>> codebasePermissionsMap = Collections.synchronizedMap(new HashMap<String, Map<PolicyEditorPermissions, Boolean>>());
-    private final Map<String, Set<CustomPermission>> customPermissionsMap = Collections.synchronizedMap(new HashMap<String, Set<CustomPermission>>());
+    private final Map<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>> permissionsMap = Collections.synchronizedMap(new HashMap<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>>());
+    private final Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> customPermissionsMap = Collections.synchronizedMap(new HashMap<PolicyIdentifier, Set<PolicyParser.PermissionEntry>>());
+
+    private KeystoreInfo keystoreInfo = new KeystoreInfo(null, null, null, null);
     private MD5SumWatcher fileWatcher;
+    private PolicyParser parser = new PolicyParser(false);
 
     PolicyFileModel(final String filepath) {
         this(new File(filepath));
@@ -82,38 +82,51 @@
     PolicyFileModel() {
     }
 
-    synchronized void setFile(final File file) {
+    synchronized boolean setFile(final File file) {
         this.file = file;
+        boolean sameFile = Objects.equals(this.file, file);
+        return !sameFile;
     }
 
     synchronized File getFile() {
         return file;
     }
 
+    synchronized PolicyParser getParser() {
+        return parser;
+    }
+
     /**
      * Open the file pointed to by the filePath field. This is either provided by the
      * "-file" command line flag, or if none given, comes from DeploymentConfiguration.
      */
-     synchronized void openAndParsePolicyFile() throws IOException, InvalidPolicyException {
+     synchronized void openAndParsePolicyFile() throws IOException, PolicyParser.ParsingException {
+        parser = new PolicyParser(false);
         fileWatcher = new MD5SumWatcher(file);
         fileWatcher.update();
-        codebasePermissionsMap.clear();
-        customPermissionsMap.clear();
+        clearPermissions();
         final FileLock fileLock = FileUtils.getFileLock(file.getAbsolutePath(), false, true);
         try {
-            // User-level policy files are expected to be short enough that loading them in as a String
-            // should not actually be *too* bad, and it's easy to work with.
-            final String contents = FileUtils.loadFileAsString(file);
-
-            final Set<PolicyEntry> entries = parsePolicyString(contents);
-            synchronized (codebasePermissionsMap) {
+            parser.read(new FileReader(file));
+            keystoreInfo = new KeystoreInfo(parser.getKeyStoreUrl(), parser.getKeyStoreType(), parser.getKeyStoreProvider(), parser.getStorePassURL());
+            final Set<PolicyParser.GrantEntry> grantEntries = new HashSet<>(Collections.list(parser.grantElements()));
+            synchronized (permissionsMap) {
                 synchronized (customPermissionsMap) {
-                    for (final PolicyEntry entry : entries) {
-                        addCodebase(entry.getCodebase());
-                        for (final PolicyEditorPermissions permission : entry.getPermissions()) {
-                            setPermission(entry.getCodebase(), permission, true);
+                    for (final PolicyParser.GrantEntry grantEntry : grantEntries) {
+                        PolicyIdentifier policyIdentifier =
+                                new PolicyIdentifier(grantEntry.signedBy, grantEntry.principals, grantEntry.codeBase);
+                        if (isDefaultPolicyIdentifier(policyIdentifier)) {
+                            policyIdentifier = PolicyIdentifier.ALL_APPLETS_IDENTIFIER;
                         }
-                        addCustomPermissions(entry.getCodebase(), entry.getCustomPermissions());
+                        addIdentifier(policyIdentifier);
+                        for (final PolicyParser.PermissionEntry permissionEntry : grantEntry.permissionEntries) {
+                            final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.fromPermissionEntry(permissionEntry);
+                            if (editorPermissions != null) {
+                                permissionsMap.get(policyIdentifier).put(editorPermissions, true);
+                            } else {
+                                customPermissionsMap.get(policyIdentifier).add(permissionEntry);
+                            }
+                        }
                     }
                 }
             }
@@ -126,63 +139,48 @@
         }
     }
 
-    static Set<PolicyEntry> parsePolicyString(final String contents) throws InvalidPolicyException {
-        final Set<PolicyEntry> policyEntries = new HashSet<>();
-        StringBuilder sb = new StringBuilder();
-
-        // Split on newlines, both \r\n and \n style, for platform-independence
-        final String[] lines = contents.split("\\r?\\n+");
-        boolean openBlock = false, closeBlock = false;
-        for (final String line : lines) {
-            // Matches eg `grant {` as well as `grant codeBase "http://redhat.com" {`
-            final Pattern openBlockPattern = Pattern.compile("grant\\s*\"?\\s*(?:codeBase)?\\s*\"?[^\"\\s]*\"?\\s*\\{");
-            final Matcher openBlockMatcher = openBlockPattern.matcher(line);
-            if (openBlockMatcher.matches()) {
-                openBlock = true;
-            }
-
-            // Matches '};', the closing block delimiter, with any amount of whitespace on either side
-            if (line.matches("\\s*\\};\\s*")) {
-                closeBlock = true;
-            }
-
-            if (openBlock) {
-                sb.append(line);
-                sb.append('\n');
-            }
-
-            if (openBlock && closeBlock) {
-                openBlock = closeBlock = false;
-                policyEntries.add(PolicyEntry.fromString(sb.toString()));
-                sb = new StringBuilder();
-            }
-        }
-        return policyEntries;
-    }
-
     /**
      * Save the policy model into the file pointed to by the filePath field.
      */
-    synchronized void savePolicyFile() throws FileNotFoundException, IOException {
-        final StringBuilder sb = new StringBuilder();
-        sb.append(AUTOGENERATED_NOTICE);
-        final String currentDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
-        sb.append("\n/* Generated by PolicyEditor at ").append(currentDate).append(" */");
-        sb.append(PlainTextFormatter.getLineSeparator());
+    synchronized void savePolicyFile() throws IOException {
+        parser = new PolicyParser(false);
         FileLock fileLock = null;
         try {
             fileLock = FileUtils.getFileLock(file.getAbsolutePath(), false, true);
-            synchronized (codebasePermissionsMap) {
-                for (final String codebase : codebasePermissionsMap.keySet()) {
-                    final Set<PolicyEditorPermissions> enabledPermissions = new HashSet<>();
-                    for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : codebasePermissionsMap.get(codebase).entrySet()) {
+            synchronized (permissionsMap) {
+                for (final PolicyIdentifier identifier : permissionsMap.keySet()) {
+                    final String codebase;
+                    if (identifier.getCodebase().isEmpty()) {
+                        codebase = null;
+                    } else {
+                        codebase = identifier.getCodebase();
+                    }
+                    final PolicyParser.GrantEntry grantEntry =
+                            new PolicyParser.GrantEntry(identifier.getSignedBy(), codebase);
+                    for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : permissionsMap.get(identifier).entrySet()) {
                         if (entry.getValue()) {
-                            enabledPermissions.add(entry.getKey());
+                            final PolicyEditorPermissions permission = entry.getKey();
+                            final String actionsString;
+                            if (permission.getActions().equals(PermissionActions.NONE)) {
+                                actionsString = null;
+                            } else {
+                                actionsString = permission.getActions().rawString();
+                            }
+                            final PolicyParser.PermissionEntry permissionEntry =
+                                    new PolicyParser.PermissionEntry(permission.getType().type,
+                                            permission.getTarget().target,
+                                            actionsString);
+                            grantEntry.add(permissionEntry);
                         }
                     }
-                    sb.append(new PolicyEntry(codebase, enabledPermissions, customPermissionsMap.get(codebase)).toString());
+                    for (final PolicyParser.PermissionEntry customPermission : customPermissionsMap.get(identifier)) {
+                        grantEntry.add(customPermission);
+                    }
+                    grantEntry.principals.addAll(identifier.getPrincipals());
+                    parser.add(grantEntry);
                 }
             }
+            parser.write(new FileWriter(file));
         } catch (final IOException e) {
             OutputController.getLogger().log(e);
         } finally {
@@ -195,40 +193,41 @@
 
             }
         }
-
-        FileUtils.saveFile(sb.toString(), file);
         fileWatcher = new MD5SumWatcher(file);
         fileWatcher.update();
     }
 
-    synchronized boolean hasChanged() throws FileNotFoundException, IOException {
+    synchronized boolean hasChanged() throws IOException {
         return fileWatcher != null && fileWatcher.update();
     }
 
-    synchronized Set<String> getCodebases() {
-        return new HashSet<>(codebasePermissionsMap.keySet());
+    synchronized Set<PolicyIdentifier> getIdentifiers() {
+        return new HashSet<>(permissionsMap.keySet());
+    }
+
+    synchronized KeystoreInfo getKeystoreInfo() {
+        return keystoreInfo;
     }
 
     /**
-     * Add a new codebase. No action is taken if the codebase has already been added.
-     * @param codebase for which a permissions mapping is required
-     * @return true iff there was already an entry for this codebase
+     * Add a new identifier. No action is taken if the identifier has already been added.
+     * @param identifier for which a permissions mapping is required
+     * @return true iff there was already an entry for this identifier
      */
-    synchronized boolean addCodebase(final String codebase) {
-        Objects.requireNonNull(codebase);
+    synchronized boolean addIdentifier(final PolicyIdentifier identifier) {
+        Objects.requireNonNull(identifier);
 
         boolean existingCodebase = true;
-        if (!codebasePermissionsMap.containsKey(codebase)) {
+        if (!permissionsMap.containsKey(identifier)) {
             final Map<PolicyEditorPermissions, Boolean> map = new HashMap<>();
             for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
                 map.put(perm, false);
             }
-            codebasePermissionsMap.put(codebase, map);
+            permissionsMap.put(identifier, map);
             existingCodebase = false;
         }
-        if (!customPermissionsMap.containsKey(codebase)) {
-            final Set<CustomPermission> set = new HashSet<>();
-            customPermissionsMap.put(codebase, set);
+        if (!customPermissionsMap.containsKey(identifier)) {
+            customPermissionsMap.put(identifier, new HashSet<PolicyParser.PermissionEntry>());
             existingCodebase = false;
         }
 
@@ -236,56 +235,56 @@
     }
 
     synchronized void clearPermissions() {
-        codebasePermissionsMap.clear();
+        permissionsMap.clear();
         clearCustomPermissions();
     }
 
-    synchronized void removeCodebase(final String codebase) {
-        Objects.requireNonNull(codebase);
-        codebasePermissionsMap.remove(codebase);
-        customPermissionsMap.remove(codebase);
+    synchronized void removeIdentifier(final PolicyIdentifier identifier) {
+        Objects.requireNonNull(identifier);
+        permissionsMap.remove(identifier);
+        customPermissionsMap.remove(identifier);
     }
 
-    synchronized void setPermission(final String codebase, final PolicyEditorPermissions permission, final boolean state) {
-        Objects.requireNonNull(codebase);
+    synchronized void setPermission(final PolicyIdentifier identifier, final PolicyEditorPermissions permission, final boolean state) {
+        Objects.requireNonNull(identifier);
         Objects.requireNonNull(permission);
-        addCodebase(codebase);
-        codebasePermissionsMap.get(codebase).put(permission, state);
+        addIdentifier(identifier);
+        permissionsMap.get(identifier).put(permission, state);
     }
 
-    synchronized boolean getPermission(final String codebase, final PolicyEditorPermissions permission) {
-        Objects.requireNonNull(codebase);
+    synchronized boolean getPermission(final PolicyIdentifier identifier, final PolicyEditorPermissions permission) {
+        Objects.requireNonNull(identifier);
         Objects.requireNonNull(permission);
-        if (!codebasePermissionsMap.containsKey(codebase)) {
+        if (!permissionsMap.containsKey(identifier)) {
             return false;
         }
-        return codebasePermissionsMap.get(codebase).get(permission);
+        return permissionsMap.get(identifier).get(permission);
     }
 
-    synchronized Map<String, Map<PolicyEditorPermissions, Boolean>> getCopyOfPermissions() {
-        return new HashMap<>(codebasePermissionsMap);
+    synchronized Map<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>> getCopyOfPermissions() {
+        return new HashMap<>(permissionsMap);
     }
 
     synchronized void clearCustomPermissions() {
         customPermissionsMap.clear();
     }
 
-    synchronized void clearCustomCodebase(final String codebase) {
-        Objects.requireNonNull(codebase);
-        if (!customPermissionsMap.containsKey(codebase)) {
+    synchronized void clearCustomIdentifier(final PolicyIdentifier identifier) {
+        Objects.requireNonNull(identifier);
+        if (!customPermissionsMap.containsKey(identifier)) {
             return;
         }
-        customPermissionsMap.get(codebase).clear();
+        customPermissionsMap.get(identifier).clear();
     }
 
-    synchronized void addCustomPermissions(final String codebase, final Collection<CustomPermission> permissions) {
-        Objects.requireNonNull(codebase);
+    synchronized void addCustomPermissions(final PolicyIdentifier identifier, final Collection<? extends PolicyParser.PermissionEntry> permissions) {
+        Objects.requireNonNull(identifier);
         Objects.requireNonNull(permissions);
-        addCodebase(codebase);
-        customPermissionsMap.get(codebase).addAll(permissions);
+        addIdentifier(identifier);
+        customPermissionsMap.get(identifier).addAll(permissions);
     }
 
-    synchronized Map<String, Set<CustomPermission>> getCopyOfCustomPermissions() {
+    synchronized Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> getCopyOfCustomPermissions() {
         return new HashMap<>(customPermissionsMap);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/security/policyeditor/PolicyIdentifier.java	Thu Jul 30 10:57:21 2015 -0400
@@ -0,0 +1,147 @@
+/*Copyright (C) 2014 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea 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 GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.security.policyeditor;
+
+import net.sourceforge.jnlp.runtime.Translator;
+import sun.security.provider.PolicyParser;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+// http://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
+public class PolicyIdentifier implements Serializable {
+
+    public static final PolicyIdentifier ALL_APPLETS_IDENTIFIER = new PolicyIdentifier(null, Collections.<PolicyParser.PrincipalEntry>emptySet(), null) {
+        @Override
+        public String toString() {
+            return Translator.R("PEGlobalSettings");
+        }
+    };
+
+    private final String signedBy;
+    private final List<PolicyParser.PrincipalEntry> principals = new ArrayList<>();
+    private final String codebase;
+
+    public PolicyIdentifier(final String signedBy, final Collection<PolicyParser.PrincipalEntry> principals, final String codebase) {
+        if (signedBy != null && signedBy.isEmpty()) {
+            this.signedBy = null;
+        } else {
+            this.signedBy = signedBy;
+        }
+        this.principals.addAll(principals);
+        if (codebase == null) {
+            this.codebase = "";
+        } else {
+            this.codebase = codebase;
+        }
+    }
+
+    public String getSignedBy() {
+        return signedBy;
+    }
+
+    public List<PolicyParser.PrincipalEntry> getPrincipals() {
+        return principals;
+    }
+
+    public String getCodebase() {
+        return codebase;
+    }
+
+    public static boolean isDefaultPolicyIdentifier(final PolicyIdentifier policyIdentifier) {
+        return policyIdentifier.getSignedBy() == null
+                && policyIdentifier.getPrincipals().isEmpty()
+                && policyIdentifier.getCodebase().isEmpty();
+    }
+
+    @Override
+    public String toString() {
+        final String newline = "<br>";
+        final List<String> props = new ArrayList<>();
+        if (!codebase.isEmpty()) {
+            props.add("codebase=" + codebase);
+            props.add(newline);
+        }
+        if (!principals.isEmpty()) {
+            props.add("principals=" + principals);
+            props.add(newline);
+        }
+        if (signedBy != null && !signedBy.isEmpty()) {
+            props.add("signedBy=" + signedBy);
+            props.add(newline);
+        }
+        final StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        if (!props.isEmpty()) {
+            for (final String prop : props.subList(0, props.size() - 1)) {
+                sb.append(prop);
+            }
+        }
+        sb.append("</html>");
+        return sb.toString();
+    }
+
+    public String toStringNoHtml() {
+        return "codebase='" + codebase + '\'' +
+                   " principals=" + principals +
+                   " signedBy='" + signedBy + '\'';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PolicyIdentifier)) return false;
+
+        final PolicyIdentifier that = (PolicyIdentifier) o;
+
+        if (signedBy != null ? !signedBy.equals(that.signedBy) : that.signedBy != null) return false;
+        if (!principals.equals(that.principals)) return false;
+        return codebase.equals(that.codebase);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = signedBy != null ? signedBy.hashCode() : 0;
+        result = 31 * result + (principals != null ? principals.hashCode() : 0);
+        result = 31 * result + (codebase.hashCode());
+        return result;
+    }
+}
--- a/netx/net/sourceforge/jnlp/util/docprovider/PolicyEditorTextsProvider.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/netx/net/sourceforge/jnlp/util/docprovider/PolicyEditorTextsProvider.java	Thu Jul 30 10:57:21 2015 -0400
@@ -68,7 +68,7 @@
     public String getSynopsis() {
         return super.getSynopsis()
                 + getFormatter().wrapParagraph(getFormatter().process(getFormatter().getBoldOpening() + " " + getId() + " " + 
-                        getFormatter().getBoldCloseNwlineBoldOpen() + getId() + " [-file] " + getFormatter().getBoldClosing() + Translator.R("PEsynopseP1") + " "+getFormatter().getBold("[-codebase] ") + Translator.R("PEsynopseP2")));
+                        getFormatter().getBoldCloseNwlineBoldOpen() + getId() + " [-file] " + getFormatter().getBoldClosing() + Translator.R("PEsynopseP1")));
     }
 
     @Override
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPermissionTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-/*Copyright (C) 2014 Red Hat, Inc.
-
-This file is part of IcedTea.
-
-IcedTea is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License as published by
-the Free Software Foundation, version 2.
-
-IcedTea 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 GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with IcedTea; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library.  Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module.  An independent module is a module which is not derived from
-or based on this library.  If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so.  If you do not wish to do so, delete this
-exception statement from your version.
- */
-
-package net.sourceforge.jnlp.security.policyeditor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-public class CustomPermissionTest {
-
-    @Test
-    public void assertFieldsPopulateCorrectly() throws Exception {
-        final CustomPermission cp = new CustomPermission("type", "target", "some,actions");
-        assertTrue("Permission type should be \"type\"", cp.type.equals("type"));
-        assertTrue("Permission target should be \"target\"", cp.target.equals("target"));
-        assertTrue("Permission actions should be \"some,actions\"", cp.actions.equals("some,actions"));
-    }
-
-    @Test
-    public void testFromStringWithoutActions() throws Exception {
-        final CustomPermission cp = CustomPermission.fromString("permission java.lang.RuntimePermission \"queuePrintJob\";");
-        assertTrue("Permission type should be \"java.lang.RuntimePermission\"", cp.type.equals("java.lang.RuntimePermission"));
-        assertTrue("Permission target should be \"queuePrintJob\"", cp.target.equals("queuePrintJob"));
-        assertTrue("Permission actions should be empty", cp.actions.isEmpty());
-    }
-
-    @Test
-    public void testFromStringWithActions() throws Exception {
-        final CustomPermission cp = CustomPermission.fromString("permission java.io.FilePermission \"*\", \"read,write\";");
-        assertTrue("Permission type should be \"java.io.FilePermission\"", cp.type.equals("java.io.FilePermission"));
-        assertTrue("Permission target should be \"*\"", cp.target.equals("*"));
-        assertTrue("Permission actions should be \"read,write\"", cp.actions.equals("read,write"));
-    }
-
-    @Test
-    public void testMissingQuotationMarks() throws Exception {
-        final CustomPermission cp = CustomPermission.fromString("permission java.io.FilePermission *, read,write;");
-        assertTrue("Custom permission should be null", cp == null);
-    }
-
-    @Test
-    public void testActionsMissingComma() throws Exception {
-        final String missingComma = "permission java.io.FilePermission \"*\" \"read,write\";";
-        final CustomPermission cp1 = CustomPermission.fromString(missingComma);
-        assertTrue("Custom permission for " + missingComma + " should be null", cp1 == null);
-    }
-
-    @Test
-    public void testActionsMissingFirstQuote() throws Exception {
-        final String missingFirstQuote = "permission java.io.FilePermission \"*\", read,write\";";
-        final CustomPermission cp2 = CustomPermission.fromString(missingFirstQuote);
-        assertTrue("Custom permission for " + missingFirstQuote + " should be null", cp2 == null);
-    }
-
-    @Test
-    public void testActionsMissingSecondQuote() throws Exception {
-        final String missingSecondQuote = "permission java.io.FilePermission \"*\", \"read,write;";
-        final CustomPermission cp3 = CustomPermission.fromString(missingSecondQuote);
-        assertTrue("Custom permission for " + missingSecondQuote + " should be null", cp3 == null);
-    }
-
-    @Test
-    public void testActionsMissingBothQuotes() throws Exception {
-        final String missingBothQuotes = "permission java.io.FilePermission \"*\", read,write;";
-        final CustomPermission cp4 = CustomPermission.fromString(missingBothQuotes);
-        assertTrue("Custom permission for " + missingBothQuotes + " should be null", cp4 == null);
-    }
-
-    @Test
-    public void testActionsMissingAllPunctuation() throws Exception {
-        final String missingAll = "permission java.io.FilePermission \"*\" read,write;";
-        final CustomPermission cp5 = CustomPermission.fromString(missingAll);
-        assertTrue("Custom permission for " + missingAll + " should be null", cp5 == null);
-    }
-
-    @Test
-    public void testToString() throws Exception {
-        final CustomPermission cp = new CustomPermission("java.io.FilePermission", "*", "read");
-        final String expected = "permission java.io.FilePermission \"*\", \"read\";";
-        assertTrue("Permissions string should have equalled " + expected, cp.toString().equals(expected));
-    }
-
-    @Test
-    public void testToStringWithNoActions() throws Exception {
-        final CustomPermission cp = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
-        final String expected = "permission java.lang.RuntimePermission \"createClassLoader\";";
-        assertEquals(expected, cp.toString());
-    }
-
-    @Test
-    public void testToStringWithEmptyActions() throws Exception {
-        final CustomPermission cp = new CustomPermission("java.lang.RuntimePermission", "createClassLoader", "");
-        final String expected = "permission java.lang.RuntimePermission \"createClassLoader\";";
-        assertEquals(expected, cp.toString());
-    }
-
-    @Test
-    public void testCompareToWithNoActions() throws Exception {
-        final CustomPermission cp1 = new CustomPermission("java.io.FilePermission", "*", "read");
-        final CustomPermission cp2 = new CustomPermission("java.io.FilePermission", "${user.home}${/}*", "read");
-        final CustomPermission cp3 = new CustomPermission("java.lang.RuntimePermission", "queuePrintJob");
-        assertTrue("cp1.compareTo(cp2) should be > 0", cp1.compareTo(cp2) > 0);
-        assertTrue("cp1.compareTo(cp1) should be 0", cp1.compareTo(cp1) == 0);
-        assertTrue("cp2.compareTo(cp3) should be < 0", cp2.compareTo(cp3) < 0);
-    }
-
-    @Test
-    public void testCompareToWithEmptyActions() throws Exception {
-        final CustomPermission cp1 = new CustomPermission("java.io.FilePermission", "*", "read");
-        final CustomPermission cp2 = new CustomPermission("java.io.FilePermission", "${user.home}${/}*", "read");
-        final CustomPermission cp3 = new CustomPermission("java.lang.RuntimePermission", "queuePrintJob", "");
-        assertTrue("cp1.compareTo(cp2) should be > 0", cp1.compareTo(cp2) > 0);
-        assertTrue("cp1.compareTo(cp1) should be 0", cp1.compareTo(cp1) == 0);
-        assertTrue("cp2.compareTo(cp3) should be < 0", cp2.compareTo(cp3) < 0);
-    }
-
-    @Test
-    public void testConstructFromEnumsToString() throws Exception {
-        final CustomPermission cp = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.READ);
-        final String permissionString = "permission java.io.FilePermission \"${user.home}\", \"read\";";
-        assertEquals(permissionString, cp.toString());
-    }
-
-    @Test
-    public void testEquals() throws Exception {
-        final CustomPermission cp1 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER);
-        final CustomPermission cp2 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER);
-        final CustomPermission cp3 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.ACCESS_THREADS);
-        assertEquals(cp1, cp2);
-        assertNotEquals(cp1, cp3);
-    }
-
-    @Test
-    public void testHashcode() throws Exception {
-        final CustomPermission cp1 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER);
-        final CustomPermission cp2 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER);
-        final CustomPermission cp3 = new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.ACCESS_THREADS);
-        assertEquals(cp1.hashCode(), cp2.hashCode());
-        assertNotEquals(cp1.hashCode(), cp3.hashCode());
-    }
-}
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewerTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/CustomPolicyViewerTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,35 +36,33 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
+import static net.sourceforge.jnlp.security.policyeditor.PolicyEditor.identifierFromCodebase;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Collection;
-
-import net.sourceforge.jnlp.security.policyeditor.CustomPermission;
-import net.sourceforge.jnlp.security.policyeditor.CustomPolicyViewer;
-import net.sourceforge.jnlp.security.policyeditor.PolicyEditor;
-
 import org.junit.Before;
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
+
+import java.util.Collection;
 
 public class CustomPolicyViewerTest {
 
     private CustomPolicyViewer viewer;
-    private static final String CODEBASE = "http://example.com";
-    private static final CustomPermission PERMISSION = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
+    private static final PolicyIdentifier IDENTIFIER = identifierFromCodebase("http://example.com");
+    private static final CustomPolicyViewer.DisplayablePermission PERMISSION = CustomPolicyViewer.DisplayablePermission.from(new PolicyParser.PermissionEntry("java.lang.RuntimePermission", "createClassLoader", null));
 
     @Before
     public void setupViewer() {
-        viewer = new CustomPolicyViewer(new PolicyEditor(null), CODEBASE);
+        viewer = new CustomPolicyViewer(new PolicyEditor(null), IDENTIFIER);
     }
 
     @Test(expected = NullPointerException.class)
     public void testConstructorWithNullPolicyEditor() throws Exception {
-        new CustomPolicyViewer(null, CODEBASE);
+        new CustomPolicyViewer(null, IDENTIFIER);
     }
 
     @Test(expected = NullPointerException.class)
@@ -121,7 +119,7 @@
 
     @Test
     public void testGetCopyOfCustomPermissionsReturnsCopy() throws Exception {
-        final Collection<CustomPermission> permissions = viewer.getCopyOfCustomPermissions();
+        final Collection<CustomPolicyViewer.DisplayablePermission> permissions = viewer.getCopyOfCustomPermissions();
         permissions.add(PERMISSION);
         assertNotEquals("Sets should be distinct", viewer.getCopyOfCustomPermissions(), permissions);
         assertNotEquals("Sizes should not match", viewer.getCopyOfCustomPermissions().size(), permissions.size());
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorControllerTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorControllerTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -39,12 +39,14 @@
 import net.sourceforge.jnlp.util.FileUtils;
 import org.junit.Before;
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
 
 import java.io.File;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -53,6 +55,23 @@
 
 public class PolicyEditorControllerTest {
 
+    private static final String SIGNED_BY = "someCA";
+    private static final String CODEBASE = "http://example.com";
+    private static final List<PolicyParser.PrincipalEntry> EMPTY_PRINCIPALS = Collections.emptyList();
+    private static final PolicyIdentifier DEFAULT_IDENTIFIER = new PolicyIdentifier(SIGNED_BY, EMPTY_PRINCIPALS, CODEBASE);
+    private static final String LINEBREAK = System.getProperty("line.separator");
+
+    private static final String EXAMPLE_POLICY_1 = "grant {" + LINEBREAK
+            + "permission some.java.permission \"somePermission\";" + LINEBREAK
+            + "};" + LINEBREAK;
+    private static final String EXAMPLE_POLICY_2 = "grant {" + LINEBREAK
+            + "permission some.other.java.permission \"somePermission\";" + LINEBREAK
+            + "};" + LINEBREAK;
+
+    private static final String CLIPBOARD_POLICY = "grant codeBase \"http://example.com\" {" + LINEBREAK
+            + "permission java.awt.AWTPermission \"accessClipboard\";" + LINEBREAK
+           + "};" + LINEBREAK;
+
     private String tempFilePath;
     private PolicyEditorController controller;
 
@@ -65,8 +84,7 @@
 
     @Test
     public void testChangesMadeInitiallyFalse() throws Exception {
-        // #setFile() counts as a change made
-        assertTrue("Controller should report changes made initially", controller.changesMade());
+        assertFalse("Controller should report changes made initially", controller.changesMade());
     }
 
     @Test
@@ -83,150 +101,145 @@
     @Test
     public void testFileHasChangedWithChange() throws Exception {
         assertFalse("Controller should report file has changed initially", controller.fileHasChanged());
-        final String codebase = "http://example.com";
-        final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.CLIPBOARD;
-        final Collection<PolicyEditorPermissions> permissions = Collections.singleton(editorPermissions);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        FileUtils.saveFile(policyEntry.toString(), new File(tempFilePath));
+        FileUtils.saveFile(EXAMPLE_POLICY_1, new File(tempFilePath));
         controller.openAndParsePolicyFile();
-        final Collection<PolicyEditorPermissions> editorPermissions2 = Collections.singleton(PolicyEditorPermissions.ALL_AWT);
-        final PolicyEntry policyEntry2 = new PolicyEntry(codebase, editorPermissions2, customPermissions);
-        FileUtils.saveFile(policyEntry2.toString(), new File(tempFilePath));
+        FileUtils.saveFile(EXAMPLE_POLICY_2, new File(tempFilePath));
         assertTrue("File should be marked changed after being externally modified", controller.fileHasChanged());
     }
 
     @Test
-    public void testInitialCodebase() throws Exception {
-        final Collection<String> initialCodebases = controller.getCodebases();
-        assertEquals("Controller should have no codebases to begin with", 0, initialCodebases.size());
+    public void testInitialIdentifier() throws Exception {
+        final Collection<PolicyIdentifier> initialIdentifiers = controller.getIdentifiers();
+        assertEquals("Controller should have no identifiers to begin with", 0, initialIdentifiers.size());
     }
 
     @Test
-    public void testAddCodebase() throws Exception {
-        final String urlString = "http://example.com";
-        controller.addCodebase(urlString);
-        final Collection<String> codebases = controller.getCodebases();
-        assertTrue("Controller should have http://example.com", codebases.contains(urlString));
-        assertEquals("Controller should only have two codebases", 1, codebases.size());
+    public void testAddIdentifier() throws Exception {
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
+        final Collection<PolicyIdentifier> identifiers = controller.getIdentifiers();
+        assertTrue("Controller should have " + DEFAULT_IDENTIFIER, identifiers.contains(DEFAULT_IDENTIFIER));
+        assertEquals("Controller should only have two identifiers", 1, identifiers.size());
     }
 
     @Test
-    public void testAddMultipleCodebases() throws Exception {
-        final Set<String> toAdd = new HashSet<String>();
-        toAdd.add("http://example.com");
-        toAdd.add("http://icedtea.classpath.org");
-        for (final String cb : toAdd) {
-            controller.addCodebase(cb);
+    public void testAddMultipleIdentifiers() throws Exception {
+        final Set<PolicyIdentifier> toAdd = new HashSet<>();
+        toAdd.add(DEFAULT_IDENTIFIER);
+        final PolicyIdentifier newIdentifier = new PolicyIdentifier(null, EMPTY_PRINCIPALS, "http://icedtea.classpath.org");
+        toAdd.add(newIdentifier);
+        for (final PolicyIdentifier id : toAdd) {
+            controller.addIdentifier(id);
         }
-        final Collection<String> codebases = controller.getCodebases();
-        for (final String codebase : toAdd) {
-            assertTrue("Controller should have " + codebase, codebases.contains(codebase));
+        final Collection<PolicyIdentifier> identifiers = controller.getIdentifiers();
+        for (final PolicyIdentifier id : toAdd) {
+            assertTrue("Controller should have " + id, identifiers.contains(id));
         }
     }
 
     @Test
-    public void testRemoveCodebase() throws Exception {
-        final String urlString = "http://example.com";
-        controller.addCodebase(urlString);
-        final Collection<String> codebases = controller.getCodebases();
-        assertTrue("Controller should have http://example.com", codebases.contains(urlString));
-        assertEquals("Controller should only have one codebase", 1, codebases.size());
-        controller.removeCodebase(urlString);
-        final Collection<String> afterRemove = controller.getCodebases();
-        assertFalse("Controller should not have http://example.com. Contained: " + afterRemove, afterRemove.contains(urlString));
-        assertEquals("Controller should have no codebases", 0, afterRemove.size());
+    public void testRemoveIdentifier() throws Exception {
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
+        final Collection<PolicyIdentifier> identifiers = controller.getIdentifiers();
+        assertTrue("Controller should have " + DEFAULT_IDENTIFIER, identifiers.contains(DEFAULT_IDENTIFIER));
+        assertEquals("Controller should only have one identifier", 1, identifiers.size());
+        controller.removeIdentifier(DEFAULT_IDENTIFIER);
+        final Collection<PolicyIdentifier> afterRemove = controller.getIdentifiers();
+        assertFalse("Controller should not have " + DEFAULT_IDENTIFIER + ". Contained: " + afterRemove, afterRemove.contains(DEFAULT_IDENTIFIER));
+        assertEquals("Controller should have no identifiers", Collections.<PolicyIdentifier>emptySet(), afterRemove);
     }
 
     @Test
-    public void testCopyPasteCodebase() throws Exception {
-        final String copyUrl = "http://example.com";
+    public void testCopyPasteIdentifiers() throws Exception {
         final String pasteUrl = "http://example.com/example";
+        final PolicyIdentifier pasteIdentifier = new PolicyIdentifier(null, EMPTY_PRINCIPALS, pasteUrl);
         final PolicyEditorPermissions clipBoard = PolicyEditorPermissions.CLIPBOARD;
-        controller.addCodebase(copyUrl);
-        controller.setPermission(copyUrl, clipBoard, Boolean.TRUE);
-        final Collection<String> beforePasteCodebases = controller.getCodebases();
-        assertTrue("Controller should contain original codebase: " + copyUrl, beforePasteCodebases.contains(copyUrl));
-        assertTrue(copyUrl + " should have " + clipBoard, controller.getPermissions(copyUrl).get(clipBoard));
-        controller.copyCodebaseToClipboard(copyUrl);
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
+        controller.setPermission(DEFAULT_IDENTIFIER, clipBoard, Boolean.TRUE);
+        final Collection<PolicyIdentifier> beforePasteIdentifiers = controller.getIdentifiers();
+        assertTrue("Controller should contain original identifier: " + DEFAULT_IDENTIFIER, beforePasteIdentifiers.contains(DEFAULT_IDENTIFIER));
+        assertTrue(DEFAULT_IDENTIFIER + " should have " + clipBoard, controller.getPermissions(DEFAULT_IDENTIFIER).get(clipBoard));
+        controller.copyPolicyEntryToClipboard(DEFAULT_IDENTIFIER);
         final PolicyEntry clipboardEntry = PolicyEditorController.getPolicyEntryFromClipboard();
-        controller.addPolicyEntry(new PolicyEntry(pasteUrl, clipboardEntry.getPermissions(), clipboardEntry.getCustomPermissions()));
-        final Collection<String> afterPasteCodebases = controller.getCodebases();
-        assertTrue("Controller should still contain original codebase: " + copyUrl, afterPasteCodebases.contains(copyUrl));
-        assertTrue("Controller should also contain pasted codebase:" + pasteUrl, afterPasteCodebases.contains(pasteUrl));
-        assertTrue(copyUrl + " should have " + clipBoard, controller.getPermissions(copyUrl).get(clipBoard));
-        assertTrue(pasteUrl + " should have " + clipBoard, controller.getPermissions(pasteUrl).get(clipBoard));
+        final PolicyEntry newEntry = new PolicyEntry.Builder()
+                .codebase(pasteUrl)
+                .permissions(clipboardEntry.getPermissions())
+                .customPermissions(clipboardEntry.getCustomPermissions())
+                .build();
+        controller.addPolicyEntry(newEntry);
+        final Collection<PolicyIdentifier> afterPasteIdentifiers = controller.getIdentifiers();
+        assertTrue("Controller should still contain original identifier: " + DEFAULT_IDENTIFIER, afterPasteIdentifiers.contains(DEFAULT_IDENTIFIER));
+        assertTrue("Controller should also contain pasted identifier:" + pasteIdentifier, afterPasteIdentifiers.contains(pasteIdentifier));
+        assertTrue(DEFAULT_IDENTIFIER + " should have " + clipBoard, controller.getPermissions(DEFAULT_IDENTIFIER).get(clipBoard));
+        assertTrue(pasteIdentifier + " should have " + clipBoard, controller.getPermissions(pasteIdentifier).get(clipBoard));
     }
 
     @Test
     public void testAddPolicyEntry() throws Exception {
-        final String codebase = "http://example.com";
         final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.CLIPBOARD;
         final Collection<PolicyEditorPermissions> permissions = Collections.singleton(editorPermissions);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
+        final Collection<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(customPermission);
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                .identifier(DEFAULT_IDENTIFIER)
+                .permissions(permissions)
+                .customPermissions(customPermissions)
+                .build();
         controller.addPolicyEntry(policyEntry);
-        final Collection<String> codebases = controller.getCodebases();
-        assertTrue("Controller should have " + codebase, codebases.contains(codebase));
-        assertEquals("Controller should only have one codebase", 1, codebases.size());
-        assertTrue("Controller should have granted " + editorPermissions, controller.getPermission(codebase, editorPermissions));
-        assertTrue("Controller should have granted " + customPermission, controller.getCustomPermissions(codebase).contains(customPermission));
+        final Collection<PolicyIdentifier> identifiers = controller.getIdentifiers();
+        assertTrue("Controller should have " + DEFAULT_IDENTIFIER, identifiers.contains(DEFAULT_IDENTIFIER));
+        assertEquals("Controller should only have one identifier", 1, identifiers.size());
+        assertTrue("Controller should have granted " + editorPermissions, controller.getPermission(DEFAULT_IDENTIFIER, editorPermissions));
+        assertTrue("Controller should have granted " + customPermission, controller.getCustomPermissions(DEFAULT_IDENTIFIER).contains(customPermission));
     }
 
     @Test
     public void testAddCustomPermissionNoActions() throws Exception {
-        final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
-        controller.addCustomPermission(codebase, customPermission);
-        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(codebase).contains(customPermission));
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader");
+        controller.addCustomPermission(DEFAULT_IDENTIFIER, customPermission);
+        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(DEFAULT_IDENTIFIER).contains(customPermission));
     }
 
     @Test
     public void testAddCustomPermissionEmptyActions() throws Exception {
-        final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader", "");
-        controller.addCustomPermission(codebase, customPermission);
-        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(codebase).contains(customPermission));
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader", "");
+        controller.addCustomPermission(DEFAULT_IDENTIFIER, customPermission);
+        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(DEFAULT_IDENTIFIER).contains(customPermission));
     }
 
     @Test
     public void testClearCustomPermissionsNoActions() throws Exception {
-        final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
-        controller.addCustomPermission(codebase, customPermission);
-        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(codebase).contains(customPermission));
-        controller.clearCustomCodebase(codebase);
-        assertEquals(0, controller.getCustomPermissions(codebase).size());
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader");
+        controller.addCustomPermission(DEFAULT_IDENTIFIER, customPermission);
+        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(DEFAULT_IDENTIFIER).contains(customPermission));
+        controller.clearCustomIdentifier(DEFAULT_IDENTIFIER);
+        assertEquals(0, controller.getCustomPermissions(DEFAULT_IDENTIFIER).size());
     }
 
     @Test
     public void testClearCustomPermissionsEmptyActions() throws Exception {
-        final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader", "");
-        controller.addCustomPermission(codebase, customPermission);
-        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(codebase).contains(customPermission));
-        controller.clearCustomCodebase(codebase);
-        assertEquals(0, controller.getCustomPermissions(codebase).size());
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader", "");
+        controller.addCustomPermission(DEFAULT_IDENTIFIER, customPermission);
+        assertTrue("Controller custom permissions should include " + customPermission + " but did not", controller.getCustomPermissions(DEFAULT_IDENTIFIER).contains(customPermission));
+        controller.clearCustomIdentifier(DEFAULT_IDENTIFIER);
+        assertEquals(0, controller.getCustomPermissions(DEFAULT_IDENTIFIER).size());
     }
 
     @Test
-    public void testReturnedCodebasesIsCopy() throws Exception {
-        final Collection<String> original = controller.getCodebases();
-        original.add("some invalid value");
-        original.remove("");
-        final Collection<String> second = controller.getCodebases();
-        assertEquals("Controller should have no codebases", 0, second.size());
+    public void testReturnedIdentifiersIsCopy() throws Exception {
+        final Collection<PolicyIdentifier> original = controller.getIdentifiers();
+        original.add(new PolicyIdentifier("invalidSigner", EMPTY_PRINCIPALS, "invalidURL"));
+        original.remove(DEFAULT_IDENTIFIER);
+        final Collection<PolicyIdentifier> second = controller.getIdentifiers();
+        assertEquals("Controller should have no identifiers", 0, second.size());
     }
 
     @Test
     public void testReturnedPermissionsMapIsCopy() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> original = controller.getPermissions("");
+        final Map<PolicyEditorPermissions, Boolean> original = controller.getPermissions(DEFAULT_IDENTIFIER);
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
             original.put(perm, true);
         }
-        final Map<PolicyEditorPermissions, Boolean> second = controller.getPermissions("");
+        final Map<PolicyEditorPermissions, Boolean> second = controller.getPermissions(DEFAULT_IDENTIFIER);
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : second.entrySet()) {
             assertFalse("Permission " + entry.getKey() + " should be false", entry.getValue());
         }
@@ -234,18 +247,18 @@
 
     @Test
     public void testReturnedCustomPermissionsSetIsCopy() throws Exception {
-        final Collection<CustomPermission> original = controller.getCustomPermissions("");
+        final Collection<PolicyParser.PermissionEntry> original = controller.getCustomPermissions(DEFAULT_IDENTIFIER);
         assertTrue("There should not be any custom permissions to start", original.isEmpty());
-        original.add(new CustomPermission("java.io.FilePermission", "*", "write"));
-        final Collection<CustomPermission> second = controller.getCustomPermissions("");
+        original.add(new CustomPolicyViewer.DisplayablePermission("java.io.FilePermission", "*", "write"));
+        final Collection<PolicyParser.PermissionEntry> second = controller.getCustomPermissions(DEFAULT_IDENTIFIER);
         assertTrue("The custom permission should not have been present", second.isEmpty());
     }
 
     @Test
     public void testDefaultPermissionsAllFalse() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> defaultMap = controller.getPermissions("");
-        controller.addCodebase("http://example.com");
-        final Map<PolicyEditorPermissions, Boolean> addedMap = controller.getPermissions("http://example.com");
+        final Map<PolicyEditorPermissions, Boolean> defaultMap = controller.getPermissions(DEFAULT_IDENTIFIER);
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
+        final Map<PolicyEditorPermissions, Boolean> addedMap = controller.getPermissions(DEFAULT_IDENTIFIER);
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : defaultMap.entrySet()) {
             assertFalse("Permission " + entry.getKey() + " should be false", entry.getValue());
         }
@@ -256,87 +269,69 @@
 
     @Test
     public void testAllPermissionsRepresented() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> defaultMap = controller.getPermissions("");
-        controller.addCodebase("http://example.com");
-        final Map<PolicyEditorPermissions, Boolean> addedMap = controller.getPermissions("http://example.com");
-        assertTrue("Default codebase permissions keyset should be the same size as enum values set",
+        final Map<PolicyEditorPermissions, Boolean> defaultMap = controller.getPermissions(DEFAULT_IDENTIFIER);
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
+        final Map<PolicyEditorPermissions, Boolean> addedMap = controller.getPermissions(DEFAULT_IDENTIFIER);
+        assertTrue("Default identifier permissions keyset should be the same size as enum values set",
                 defaultMap.keySet().size() == PolicyEditorPermissions.values().length);
-        assertTrue("Added codebase permissions keyset should be the same size as enum values set",
+        assertTrue("Added identifier permissions keyset should be the same size as enum values set",
                 addedMap.keySet().size() == PolicyEditorPermissions.values().length);
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
-            assertTrue("Permission " + perm + " should be in the editor's codebase keyset", defaultMap.keySet().contains(perm));
+            assertTrue("Permission " + perm + " should be in the editor's identifier keyset", defaultMap.keySet().contains(perm));
         }
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
-            assertTrue("Permission " + perm + " should be in the editor's codebase keyset", addedMap.keySet().contains(perm));
+            assertTrue("Permission " + perm + " should be in the editor's identifier keyset", addedMap.keySet().contains(perm));
         }
     }
 
     @Test
     public void testSetGetPermission() throws Exception {
-        final String codebase = "http://example.com";
-        controller.addCodebase(codebase);
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
         final PolicyEditorPermissions permission = PolicyEditorPermissions.CLIPBOARD;
-        assertFalse("Clipboard permission should not be initially granted", controller.getPermission(codebase, permission));
-        controller.setPermission(codebase, permission, true);
-        assertTrue("Clipboard permission should be granted after being set", controller.getPermission(codebase, permission));
+        assertFalse("Clipboard permission should not be initially granted", controller.getPermission(DEFAULT_IDENTIFIER, permission));
+        controller.setPermission(DEFAULT_IDENTIFIER, permission, true);
+        assertTrue("Clipboard permission should be granted after being set", controller.getPermission(DEFAULT_IDENTIFIER, permission));
     }
 
     @Test
     public void testClearPermission() throws Exception {
-        final String codebase = "http://example.com";
-        controller.addCodebase(codebase);
+        controller.addIdentifier(DEFAULT_IDENTIFIER);
         final PolicyEditorPermissions permission = PolicyEditorPermissions.CLIPBOARD;
-        assertFalse("Clipboard permission should not be initially granted", controller.getPermission(codebase, permission));
-        controller.setPermission(codebase, permission, true);
-        assertTrue("Clipboard permission should be granted after being set", controller.getPermission(codebase, permission));
+        assertFalse("Clipboard permission should not be initially granted", controller.getPermission(DEFAULT_IDENTIFIER, permission));
+        controller.setPermission(DEFAULT_IDENTIFIER, permission, true);
+        assertTrue("Clipboard permission should be granted after being set", controller.getPermission(DEFAULT_IDENTIFIER, permission));
         controller.clearPermissions();
-        for (final String cb : controller.getCodebases()) {
-            for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : controller.getPermissions(cb).entrySet()) {
-                assertFalse("Permission " + entry.getKey() + " should be false for codebase " + cb, entry.getValue());
+        for (final PolicyIdentifier id : controller.getIdentifiers()) {
+            for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : controller.getPermissions(id).entrySet()) {
+                assertFalse("Permission " + entry.getKey() + " should be false for identifier " + id, entry.getValue());
             }
         }
-        assertEquals(0, controller.getCodebases().size());
+        assertEquals(0, controller.getIdentifiers().size());
     }
 
     @Test
-    public void testCodebaseTrailingSlashesDoNotMatch() throws Exception {
-        final Collection<String> toAdd = Arrays.asList("http://redhat.com", "http://redhat.com/");
-        for (final String cb : toAdd) {
-            controller.addCodebase(cb);
+    public void testIdentifierCodebaseTrailingSlashesDoNotMatch() throws Exception {
+        final PolicyIdentifier firstId = new PolicyIdentifier(SIGNED_BY, EMPTY_PRINCIPALS, "http://example.com");
+        final PolicyIdentifier secondId = new PolicyIdentifier(SIGNED_BY, EMPTY_PRINCIPALS, "http://example.com");
+        final Collection<PolicyIdentifier> toAdd = Arrays.asList(firstId, secondId);
+        for (final PolicyIdentifier id : toAdd) {
+            controller.addIdentifier(id);
         }
-        final Collection<String> codebases = controller.getCodebases();
-        for (final String codebase : toAdd) {
-            assertTrue("Controller should have " + codebase, codebases.contains(codebase));
+        final Collection<PolicyIdentifier> identifiers = controller.getIdentifiers();
+        for (final PolicyIdentifier id : toAdd) {
+            assertTrue("Controller should have " + id, identifiers.contains(id));
         }
     }
 
     @Test
     public void testOpenAndParsePolicyFile() throws Exception {
-        final String codebase = "http://example.com";
-        final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.CLIPBOARD;
-        final Collection<PolicyEditorPermissions> permissions = Collections.singleton(editorPermissions);
-        final CustomPermission customPermission = new CustomPermission("com.example.CustomPermission", PermissionTarget.USER_HOME.target, PermissionActions.FILE_ALL.rawString());
-        final Collection<CustomPermission> customPermissions = new HashSet<>(Collections.singleton(customPermission));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        FileUtils.saveFile(policyEntry.toString(), new File(tempFilePath));
+        final PolicyIdentifier exampleIdentifier = new PolicyIdentifier(null, Collections.<PolicyParser.PrincipalEntry>emptyList(), "http://example.com");
+        FileUtils.saveFile(CLIPBOARD_POLICY, new File(tempFilePath));
         controller.openAndParsePolicyFile();
-        assertEquals("Controller should have one codebase", 1, controller.getCodebases().size());
-        assertTrue("Controller should have codebase " + codebase, controller.getCodebases().contains(codebase));
-        assertTrue("Controller should grant " + editorPermissions, controller.getPermission(codebase, editorPermissions));
-        assertEquals("Custom permission sets were not equal", customPermissions, controller.getCustomPermissions(codebase));
+        assertEquals("Controller should have one identifier", 1, controller.getIdentifiers().size());
+        assertTrue("Controller should have identifier " + exampleIdentifier, controller.getIdentifiers().contains(exampleIdentifier));
+        assertTrue("Controller should grant " + PolicyEditorPermissions.CLIPBOARD + " got: " + controller.getPermissions(exampleIdentifier) + " and: " + controller.getCustomPermissions(exampleIdentifier), controller.getPermission(exampleIdentifier, PolicyEditorPermissions.CLIPBOARD));
+        assertEquals("Custom permission set should have been empty", Collections.<PolicyParser.PermissionEntry>emptySet(), controller.getCustomPermissions(exampleIdentifier));
     }
 
-    @Test
-    public void testSavePolicyFile() throws Exception {
-        final String codebase = "http://example.com";
-        final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.CLIPBOARD;
-        final Collection<PolicyEditorPermissions> permissions = Collections.singleton(editorPermissions);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        controller.addPolicyEntry(policyEntry);
-        controller.savePolicyFile();
-        final String fileContent = FileUtils.loadFileAsString(new File(tempFilePath));
-        assertTrue("Saved file should contain policy entry as string", fileContent.contains(policyEntry.toString()));
-    }
 }
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorParsingTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorParsingTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,17 +36,24 @@
 package net.sourceforge.jnlp.security.policyeditor;
 
 import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
-import net.sourceforge.jnlp.annotations.KnownToFail;
 import net.sourceforge.jnlp.util.FileUtils;
 import net.sourceforge.jnlp.util.docprovider.formatters.formatters.PlainTextFormatter;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import org.junit.Before;
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
 
 public class PolicyEditorParsingTest {
 
+    private static final Collection<PolicyParser.PrincipalEntry> EMPTY_PRINCIPALS = Collections.emptyList();
+    private static final String EXAMPLE_CODEBASE = "http://example.com";
+    private static final PolicyIdentifier DEFAULT_IDENTIFIER = new PolicyIdentifier(null, EMPTY_PRINCIPALS, null);
+    private static final PolicyIdentifier EXAMPLE_IDENTIFIER = new PolicyIdentifier(null, EMPTY_PRINCIPALS, EXAMPLE_CODEBASE);
+
     private File file;
     private PolicyFileModel policyFileModel = new PolicyFileModel();
     private Map<PolicyEditorPermissions, Boolean> permissions;
@@ -110,18 +117,18 @@
         file.deleteOnExit();
     }
 
-    private void setupTest(final String policyContents, final String codebase) throws Exception {
+    private void setupTest(final String policyContents, final PolicyIdentifier identifier) throws Exception {
         FileUtils.saveFile(policyContents, file);
         policyFileModel = new PolicyFileModel(file.getCanonicalFile());
         policyFileModel.openAndParsePolicyFile();
-        policyFileModel.addCodebase("");
-        policyFileModel.addCodebase(codebase);
-        permissions = policyFileModel.getCopyOfPermissions().get(codebase);
+        policyFileModel.addIdentifier(DEFAULT_IDENTIFIER);
+        policyFileModel.addIdentifier(identifier);
+        permissions = policyFileModel.getCopyOfPermissions().get(identifier);
     }
 
     @Test
     public void testNormalPolicy() throws Exception {
-        setupTest(NORMAL_POLICY, "");
+        setupTest(NORMAL_POLICY, DEFAULT_IDENTIFIER);
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
@@ -133,7 +140,7 @@
     @Test
     public void testNormalPolicyWithCRLFEndings() throws Exception {
         // This is the same test as testNormalPolicy on systems where the line separator is \r\n
-        setupTest(NORMAL_POLICY_CRLF, "");
+        setupTest(NORMAL_POLICY_CRLF, DEFAULT_IDENTIFIER);
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
@@ -145,7 +152,7 @@
     @Test
     public void testNormalPolicyWithLFEndings() throws Exception {
         // This is the same test as testNormalPolicy on systems where the line separator is \n
-        setupTest(NORMAL_POLICY_LF, "");
+        setupTest(NORMAL_POLICY_LF, DEFAULT_IDENTIFIER);
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
@@ -157,7 +164,7 @@
     @Test
     public void testNormalPolicyWithMixedEndings() throws Exception {
         // This is the same test as testNormalPolicy on systems where the line separator is \n
-        setupTest(NORMAL_POLICY_MIXED_ENDINGS, "");
+        setupTest(NORMAL_POLICY_MIXED_ENDINGS, DEFAULT_IDENTIFIER);
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
@@ -168,7 +175,7 @@
 
     @Test
     public void testCommentHeaders() throws Exception {
-        setupTest(COMMENT_HEADER, "");
+        setupTest(COMMENT_HEADER, DEFAULT_IDENTIFIER);
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             assertFalse("Permission " + perm + " should not be granted", permissions.get(perm));
         }
@@ -176,7 +183,7 @@
 
     @Test
     public void testCommentBlockedPermission() throws Exception {
-        setupTest(COMMENT_BLOCKED_PERMISSION, "");
+        setupTest(COMMENT_BLOCKED_PERMISSION, DEFAULT_IDENTIFIER);
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             assertFalse("Permission " + perm + " should not be granted", permissions.get(perm));
         }
@@ -184,7 +191,7 @@
 
     @Test
     public void testCommentBlockedPolicy() throws Exception {
-        setupTest(COMMENT_BLOCKED_POLICY, "");
+        setupTest(COMMENT_BLOCKED_POLICY, DEFAULT_IDENTIFIER);
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             assertFalse("Permission " + perm + " should not be granted", permissions.get(perm));
         }
@@ -192,7 +199,7 @@
 
     @Test
     public void testCommentedLine() throws Exception {
-        setupTest(COMMENTED_PERMISSION, "");
+        setupTest(COMMENTED_PERMISSION, DEFAULT_IDENTIFIER);
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             assertFalse("Permission " + perm + " should not be granted", permissions.get(perm));
         }
@@ -200,7 +207,7 @@
 
     @Test
     public void testMultiplePermissions() throws Exception {
-        setupTest(MULTIPLE_PERMISSION_POLICY, "");
+        setupTest(MULTIPLE_PERMISSION_POLICY, DEFAULT_IDENTIFIER);
 
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         assertTrue("Permissions should include WRITE_LOCAL_FILES", permissions.get(PolicyEditorPermissions.WRITE_LOCAL_FILES));
@@ -211,10 +218,9 @@
         }
     }
 
-    @KnownToFail
     @Test
     public void testMultiplePermissionsPerLine() throws Exception {
-        setupTest(MULTIPLE_PERMISSIONS_PER_LINE, "");
+        setupTest(MULTIPLE_PERMISSIONS_PER_LINE, DEFAULT_IDENTIFIER);
 
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         assertTrue("Permissions should include WRITE_LOCAL_FILES", permissions.get(PolicyEditorPermissions.WRITE_LOCAL_FILES));
@@ -225,10 +231,9 @@
         }
     }
 
-    @KnownToFail
     @Test
     public void testMissingWhitespace() throws Exception {
-        setupTest(MISSING_WHITESPACE_POLICY, "");
+        setupTest(MISSING_WHITESPACE_POLICY, DEFAULT_IDENTIFIER);
 
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
@@ -240,7 +245,7 @@
 
     @Test
     public void testPolicyWithCodebase() throws Exception {
-        setupTest(CODEBASE_POLICY, "http://example.com");
+        setupTest(CODEBASE_POLICY, EXAMPLE_IDENTIFIER);
 
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
@@ -254,7 +259,7 @@
     public void testCodebaseTrailingSlashesDoNotMatch() throws Exception {
         // note the trailing '/' - looks like the same URL but is not. JDK PolicyTool considers these as
         // different codeBases, so so does PolicyEditor
-        setupTest(CODEBASE_POLICY, "http://example.com/");
+        setupTest(CODEBASE_POLICY, EXAMPLE_IDENTIFIER);
 
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
@@ -265,7 +270,7 @@
 
     @Test
     public void testCommentAfterPermission() throws Exception {
-        setupTest(COMMENT_AFTER_PERMISSION, "");
+        setupTest(COMMENT_AFTER_PERMISSION, DEFAULT_IDENTIFIER);
 
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
@@ -277,7 +282,7 @@
 
     @Test
     public void testNormalPolicyWithHeader() throws Exception {
-        setupTest(NORMAL_POLICY_WITH_HEADER, "");
+        setupTest(NORMAL_POLICY_WITH_HEADER, DEFAULT_IDENTIFIER);
         assertTrue("Permissions should include READ_LOCAL_FILES", permissions.get(PolicyEditorPermissions.READ_LOCAL_FILES));
         for (final PolicyEditorPermissions perm : permissions.keySet()) {
             if (!perm.equals(PolicyEditorPermissions.READ_LOCAL_FILES)) {
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,10 +36,9 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
-import java.util.regex.Pattern;
+import org.junit.Test;
+
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
 
 public class PolicyEditorPermissionsTest {
 
@@ -65,39 +64,4 @@
         }
     }
 
-    @Test
-    public void testActionsRegex() throws Exception {
-        final Pattern pattern = CustomPermission.ACTIONS_PERMISSION;
-
-        final String actionsPermission = "permission java.io.FilePermission \"${user.home}\", \"read\";";
-        final String targetPermission = "permission java.io.RuntimePermission \"queuePrintJob\";";
-        final String badPermission = "permission java.io.FilePermission user.home read;";
-
-        assertTrue(actionsPermission + " should match", pattern.matcher(actionsPermission).matches());
-        assertFalse(targetPermission + " should not match", pattern.matcher(targetPermission).matches());
-        assertFalse(badPermission + " should not match", pattern.matcher(badPermission).matches());
-    }
-
-    @Test
-    public void testTargetRegex() throws Exception {
-        final Pattern pattern = CustomPermission.TARGET_PERMISSION;
-
-        final String actionsPermission = "permission java.io.FilePermission \"${user.home}\", \"read\";";
-        final String targetPermission = "permission java.io.RuntimePermission \"queuePrintJob\";";
-        final String badPermission = "permission java.io.FilePermission user.home read;";
-
-        assertFalse(actionsPermission + " should not match", pattern.matcher(actionsPermission).matches());
-        assertTrue(targetPermission + " should match", pattern.matcher(targetPermission).matches());
-        assertFalse(badPermission + " should not match", pattern.matcher(badPermission).matches());
-    }
-
-    @Test
-    public void testRegexesAgainstBadPermissionNames() throws Exception {
-        final Pattern targetPattern = CustomPermission.TARGET_PERMISSION;
-        final Pattern actionsPattern = CustomPermission.ACTIONS_PERMISSION;
-        final String badPermission = "permission abc123^$% \"target\", \"actions\"";
-
-        assertFalse(badPermission + " should not match", targetPattern.matcher(badPermission).matches());
-        assertFalse(badPermission + " should not match", actionsPattern.matcher(badPermission).matches());
-    }
 }
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -36,6 +36,7 @@
 
 package net.sourceforge.jnlp.security.policyeditor;
 
+import static net.sourceforge.jnlp.security.policyeditor.PolicyEditor.identifierFromCodebase;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -48,6 +49,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
 
 public class PolicyEditorTest {
 
@@ -58,6 +60,7 @@
     public void setNewTempfile() throws Exception {
         tempFilePath = File.createTempFile("policyeditor", null).getCanonicalPath();
         editor = new PolicyEditor(tempFilePath);
+        editor.openPolicyFileSynchronously();
     }
 
     @Test
@@ -65,13 +68,14 @@
         final Collection<String> initialCodebases = editor.getCodebases();
         assertTrue("Editor should have one codebase to begin with", initialCodebases.size() == 1);
         assertTrue("Editor's initial codebase should be \"\" (empty string)",
-                initialCodebases.toArray(new String[0])[0].equals(""));
+                          initialCodebases.toArray(new String[initialCodebases.size()])[0].equals(""));
     }
 
     @Test
     public void testAddCodebase() throws Exception {
         final String urlString = "http://example.com";
-        editor.addNewCodebase(urlString);
+        final PolicyIdentifier identifier = identifierFromCodebase(urlString);
+        editor.addNewEntry(identifier);
         final Collection<String> codebases = editor.getCodebases();
         assertTrue("Editor should have default codebase", codebases.contains(""));
         assertTrue("Editor should have http://example.com", codebases.contains(urlString));
@@ -80,11 +84,11 @@
 
     @Test
     public void addMultipleCodebases() throws Exception {
-        final Set<String> toAdd = new HashSet<String>();
+        final Set<String> toAdd = new HashSet<>();
         toAdd.add("http://example.com");
         toAdd.add("http://icedtea.classpath.org");
         for (final String cb : toAdd) {
-            editor.addNewCodebase(cb);
+            editor.addNewEntry(identifierFromCodebase(cb));
         }
         final Collection<String> codebases = editor.getCodebases();
         assertTrue("Editor should have default codebase", codebases.contains(""));
@@ -96,7 +100,7 @@
     @Test
     public void testAddInvalidUrlCodebase() throws Exception {
         final String invalidUrl = "url.com"; // missing protocol -> invalid
-        editor.addNewCodebase(invalidUrl);
+        editor.addNewEntry(identifierFromCodebase(invalidUrl));
         final Collection<String> codebases = editor.getCodebases();
         assertTrue("Editor should have default codebase", codebases.contains(""));
         assertTrue("Editor should only have default codebase", codebases.size() == 1);
@@ -105,12 +109,13 @@
     @Test
     public void testRemoveCodebase() throws Exception {
         final String urlString = "http://example.com";
-        editor.addNewCodebase(urlString);
+        final PolicyIdentifier identifier = identifierFromCodebase(urlString);
+        editor.addNewEntry(identifier);
         final Collection<String> codebases = editor.getCodebases();
         assertTrue("Editor should have default codebase", codebases.contains(""));
         assertTrue("Editor should have http://example.com", codebases.contains(urlString));
         assertEquals("Editor should only have two codebases", codebases.size(), 2);
-        editor.removeCodebase(urlString);
+        editor.removeIdentifier(identifier);
         final Collection<String> afterRemove = editor.getCodebases();
         assertTrue("Editor should have default codebase", afterRemove.contains(""));
         assertFalse("Editor should not have http://example.com. Contained: " + afterRemove, afterRemove.contains(urlString));
@@ -122,16 +127,18 @@
         final String originalUrl = "http://example.com";
         final String renamedUrl = "http://example.com/example";
         final PolicyEditorPermissions clipBoard = PolicyEditorPermissions.CLIPBOARD;
-        editor.addNewCodebase(originalUrl);
-        editor.setPermission(originalUrl, clipBoard, Boolean.TRUE);
+        final PolicyIdentifier identifier = identifierFromCodebase(originalUrl);
+        editor.addNewEntry(identifier);
+        editor.setPermission(identifier, clipBoard, Boolean.TRUE);
         final Collection<String> beforeRenameCodebases = editor.getCodebases();
         assertTrue("Editor should contain " + originalUrl, beforeRenameCodebases.contains(originalUrl));
-        assertTrue(originalUrl + " should have " + clipBoard, editor.getPermissions(originalUrl).get(clipBoard));
-        editor.renameCodebase(originalUrl, renamedUrl);
+        assertTrue(originalUrl + " should have " + clipBoard, editor.getPermissions(identifier).get(clipBoard));
+        editor.modifyCodebase(identifier, renamedUrl);
         final Collection<String> afterRenamedCodebases = editor.getCodebases();
         assertFalse("Editor should not contain old codebase: " + originalUrl, afterRenamedCodebases.contains(originalUrl));
         assertTrue("Editor should contain new codebase name: " + renamedUrl, afterRenamedCodebases.contains(renamedUrl));
-        assertTrue("Renamed " + renamedUrl + " should have " + clipBoard, editor.getPermissions(renamedUrl).get(clipBoard));
+        final PolicyIdentifier renamedIdentifier = identifierFromCodebase(renamedUrl);
+        assertTrue("Renamed " + renamedUrl + " should have " + clipBoard, editor.getPermissions(renamedIdentifier).get(clipBoard));
     }
 
     @Test
@@ -139,54 +146,60 @@
         final String copyUrl = "http://example.com";
         final String pasteUrl = "http://example.com/example";
         final PolicyEditorPermissions clipBoard = PolicyEditorPermissions.CLIPBOARD;
-        editor.addNewCodebase(copyUrl);
-        editor.setPermission(copyUrl, clipBoard, Boolean.TRUE);
+        final PolicyIdentifier identifier = identifierFromCodebase(copyUrl);
+        editor.addNewEntry(identifier);
+        editor.setPermission(identifier, clipBoard, Boolean.TRUE);
         final Collection<String> beforePasteCodebases = editor.getCodebases();
         assertTrue("Editor should contain original codebase: " + copyUrl, beforePasteCodebases.contains(copyUrl));
-        assertTrue(copyUrl + " should have " + clipBoard, editor.getPermissions(copyUrl).get(clipBoard));
-        editor.copyCodebase(copyUrl);
-        editor.pasteCodebase(pasteUrl);
+        assertTrue(copyUrl + " should have " + clipBoard, editor.getPermissions(identifier).get(clipBoard));
+        editor.copyEntry(identifier);
+        final PolicyIdentifier pastedIdentifier = identifierFromCodebase(pasteUrl);
+        editor.pasteEntry(pastedIdentifier);
         final Collection<String> afterPasteCodebases = editor.getCodebases();
         assertTrue("Editor should still contain original codebase: " + copyUrl, afterPasteCodebases.contains(copyUrl));
         assertTrue("Editor should also contain pasted codebase:" + pasteUrl, afterPasteCodebases.contains(pasteUrl));
-        assertTrue(copyUrl + " should have " + clipBoard, editor.getPermissions(copyUrl).get(clipBoard));
-        assertTrue(pasteUrl + " should have " + clipBoard, editor.getPermissions(pasteUrl).get(clipBoard));
+        assertTrue(copyUrl + " should have " + clipBoard, editor.getPermissions(identifier).get(clipBoard));
+        assertTrue(pasteUrl + " should have " + clipBoard, editor.getPermissions(pastedIdentifier).get(clipBoard));
     }
 
     @Test
     public void testAddCustomPermissionNoActions() throws Exception {
         final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
-        editor.addCustomPermission(codebase, customPermission);
-        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(codebase).contains(customPermission));
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader");
+        final PolicyIdentifier identifier = identifierFromCodebase(codebase);
+        editor.addCustomPermission(identifier, customPermission);
+        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(identifier).contains(customPermission));
     }
 
     @Test
     public void testAddCustomPermissionEmptyActions() throws Exception {
         final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader", "");
-        editor.addCustomPermission(codebase, customPermission);
-        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(codebase).contains(customPermission));
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader", "");
+        final PolicyIdentifier identifier = identifierFromCodebase(codebase);
+        editor.addCustomPermission(identifier, customPermission);
+        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(identifier).contains(customPermission));
     }
 
     @Test
     public void testClearCustomPermissionsNoActions() throws Exception {
         final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader");
-        editor.addCustomPermission(codebase, customPermission);
-        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(codebase).contains(customPermission));
-        editor.clearCustomPermissions(codebase);
-        assertEquals(0, editor.getCustomPermissions(codebase).size());
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader");
+        final PolicyIdentifier identifier = identifierFromCodebase(codebase);
+        editor.addCustomPermission(identifier, customPermission);
+        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(identifier).contains(customPermission));
+        editor.clearCustomPermissions(identifier);
+        assertEquals(0, editor.getCustomPermissions(identifier).size());
     }
 
     @Test
     public void testClearCustomPermissionsEmptyActions() throws Exception {
         final String codebase = "http://example.com";
-        final CustomPermission customPermission = new CustomPermission("java.lang.RuntimePermission", "createClassLoader", "");
-        editor.addCustomPermission(codebase, customPermission);
-        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(codebase).contains(customPermission));
-        editor.clearCustomPermissions(codebase);
-        assertEquals(0, editor.getCustomPermissions(codebase).size());
+        final CustomPolicyViewer.DisplayablePermission customPermission = new CustomPolicyViewer.DisplayablePermission("java.lang.RuntimePermission", "createClassLoader", "");
+        final PolicyIdentifier identifier = identifierFromCodebase(codebase);
+        editor.addCustomPermission(identifier, customPermission);
+        assertTrue("Editor custom permissions should include " + customPermission + " but did not", editor.getCustomPermissions(identifier).contains(customPermission));
+        editor.clearCustomPermissions(identifier);
+        assertEquals(0, editor.getCustomPermissions(identifier).size());
     }
 
     @Test
@@ -201,11 +214,11 @@
 
     @Test
     public void testReturnedPermissionsMapIsCopy() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> original = editor.getPermissions("");
+        final Map<PolicyEditorPermissions, Boolean> original = editor.getPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
             original.put(perm, true);
         }
-        final Map<PolicyEditorPermissions, Boolean> second = editor.getPermissions("");
+        final Map<PolicyEditorPermissions, Boolean> second = editor.getPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : second.entrySet()) {
             assertFalse("Permission " + entry.getKey() + " should be false", entry.getValue());
         }
@@ -213,18 +226,19 @@
 
     @Test
     public void testReturnedCustomPermissionsSetIsCopy() throws Exception {
-        final Collection<CustomPermission> original = editor.getCustomPermissions("");
+        final Collection<PolicyParser.PermissionEntry> original = editor.getCustomPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
         assertTrue("There should not be any custom permissions to start", original.isEmpty());
-        original.add(new CustomPermission("java.io.FilePermission", "*", "write"));
-        final Collection<CustomPermission> second = editor.getCustomPermissions("");
+        original.add(new CustomPolicyViewer.DisplayablePermission("java.io.FilePermission", "*", "write"));
+        final Collection<PolicyParser.PermissionEntry> second = editor.getCustomPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
         assertTrue("The custom permission should not have been present", second.isEmpty());
     }
 
     @Test
     public void testDefaultPermissionsAllFalse() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> defaultMap = editor.getPermissions("");
-        editor.addNewCodebase("http://example.com");
-        final Map<PolicyEditorPermissions, Boolean> addedMap = editor.getPermissions("http://example.com");
+        final Map<PolicyEditorPermissions, Boolean> defaultMap = editor.getPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
+        final PolicyIdentifier exampleIdentifier = identifierFromCodebase("http://example.com");
+        editor.addNewEntry(exampleIdentifier);
+        final Map<PolicyEditorPermissions, Boolean> addedMap = editor.getPermissions(exampleIdentifier);
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : defaultMap.entrySet()) {
             assertFalse("Permission " + entry.getKey() + " should be false", entry.getValue());
         }
@@ -235,9 +249,10 @@
 
     @Test
     public void testAllPermissionsRepresented() throws Exception {
-        final Map<PolicyEditorPermissions, Boolean> defaultMap = editor.getPermissions("");
-        editor.addNewCodebase("http://example.com");
-        final Map<PolicyEditorPermissions, Boolean> addedMap = editor.getPermissions("http://example.com");
+        final Map<PolicyEditorPermissions, Boolean> defaultMap = editor.getPermissions(PolicyIdentifier.ALL_APPLETS_IDENTIFIER);
+        final PolicyIdentifier exampleIdentifier = identifierFromCodebase("http://example.com");
+        editor.addNewEntry(exampleIdentifier);
+        final Map<PolicyEditorPermissions, Boolean> addedMap = editor.getPermissions(exampleIdentifier);
         assertTrue("Default codebase permissions keyset should be the same size as enum values set",
                 defaultMap.keySet().size() == PolicyEditorPermissions.values().length);
         assertTrue("Added codebase permissions keyset should be the same size as enum values set",
@@ -252,11 +267,11 @@
 
     @Test
     public void testCodebaseTrailingSlashesDoNotMatch() throws Exception {
-        final Set<String> toAdd = new HashSet<String>();
-        toAdd.add("http://redhat.com");
-        toAdd.add("http://redhat.com/");
+        final Set<String> toAdd = new HashSet<>();
+        toAdd.add("http://example.com");
+        toAdd.add("http://example.com/");
         for (final String cb : toAdd) {
-            editor.addNewCodebase(cb);
+            editor.addNewEntry(identifierFromCodebase(cb));
         }
         final Collection<String> codebases = editor.getCodebases();
         assertTrue("Editor should have default codebase", codebases.contains(""));
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEntryTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEntryTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -37,52 +37,56 @@
 package net.sourceforge.jnlp.security.policyeditor;
 
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
 /**
  * PolicyEntryTest does not test the various parsing scenarios as those are tested in PolicyEditorParsingTest
  */
 public class PolicyEntryTest {
 
-    public static final String POLICY_ENTRY_STRING = "\ngrant codeBase \"http://example.com\" {\n" +
-                                                        "\tpermission java.awt.AWTPermission \"accessClipboard\";\n" +
-                                                    "};\n";
-    public static final String INVALID_POLICY_STRING = "\ngrant codeBase http://example.com {\n" +
-                                                        "\tpermission java.awt.AWTPermission \"accessClipboard\";" +
-                                                    "}";
-
     @Test
     public void testGetCodebase() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        assertEquals("Codebase should equal input", codebase, policyEntry.getCodebase());
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                .codebase(codebase)
+                .permissions(permissions)
+                .customPermissions(customPermissions)
+                .build();
+        assertEquals("Codebase should equal input", codebase, policyEntry.getPolicyIdentifier().getCodebase());
     }
 
     @Test
     public void testNullCodebaseConvertsToEmpty() throws Exception {
         final String codebase = null;
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        assertEquals("Null codebase should produce empty string", "", policyEntry.getCodebase());
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
+        assertEquals("Null codebase should produce empty string", "", policyEntry.getPolicyIdentifier().getCodebase());
     }
 
     @Test
     public void testGetPermissions() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
         assertEquals("Permissions set should equal input", permissions, policyEntry.getPermissions());
     }
 
@@ -94,8 +98,12 @@
             add(PolicyEditorPermissions.CLIPBOARD);
             add(PolicyEditorPermissions.NETWORK);
         }};
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
         assertEquals("Permissions set should equal input", permissions, policyEntry.getPermissions());
     }
 
@@ -103,8 +111,12 @@
     public void testGetCustomPermissions() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final Set<? extends PolicyParser.PermissionEntry> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
         assertEquals("Custom permissions set should equal input", customPermissions, policyEntry.getCustomPermissions());
     }
 
@@ -112,80 +124,63 @@
     public void testGetCustomPermissions2() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = new HashSet<CustomPermission>(){{
-            add(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-            add(new CustomPermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER));
+        final Set<? extends PolicyParser.PermissionEntry> customPermissions = new HashSet<CustomPolicyViewer.DisplayablePermission>(){{
+            add(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+            add(new CustomPolicyViewer.DisplayablePermission(PermissionType.RUNTIME_PERMISSION, PermissionTarget.CLASSLOADER));
         }};
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
         assertEquals("Custom permissions set should equal input", customPermissions, policyEntry.getCustomPermissions());
     }
 
     @Test(expected = NullPointerException.class)
     public void testEntryWithNullPermissions() throws Exception {
         final String codebase = "http://example.com";
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        new PolicyEntry(codebase, null, customPermissions);
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        new PolicyEntry.Builder()
+                .codebase(codebase)
+                .permissions(null)
+                .customPermissions(customPermissions)
+                .build();
     }
 
     @Test(expected = NullPointerException.class)
     public void testEntryWithNullCustomPermissions() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        new PolicyEntry(codebase, permissions, null);
+        new PolicyEntry.Builder()
+                .codebase(codebase)
+                .permissions(permissions)
+                .customPermissions(null)
+                .build();
     }
 
-    @Test
+    @Test(expected = NullPointerException.class)
     public void testNullPermissionsNotAllowed() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(null);
-        final Set<CustomPermission> customPermissions = Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        assertFalse("Permissions set should not contain null element", policyEntry.getPermissions().contains(null));
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY));
+        new PolicyEntry.Builder()
+            .codebase(codebase)
+            .permissions(permissions)
+            .customPermissions(customPermissions)
+            .build();
     }
 
     @Test
     public void testNullCustomPermissionsNotAllowed() throws Exception {
         final String codebase = "http://example.com";
         final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final Set<CustomPermission> customPermissions = Collections.singleton(null);
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
+        final Set<CustomPolicyViewer.DisplayablePermission> customPermissions = Collections.singleton(null);
+        final PolicyEntry policyEntry = new PolicyEntry.Builder()
+                                                .codebase(codebase)
+                                                .permissions(permissions)
+                                                .customPermissions(customPermissions)
+                                                .build();
         assertFalse("Custom permissions set should not contain null element", policyEntry.getCustomPermissions().contains(null));
     }
 
-    @Test
-    public void testFromString() throws Exception {
-        final PolicyEntry entry = PolicyEntry.fromString(POLICY_ENTRY_STRING);
-        assertEquals("Codebase should equal http://example.com", "http://example.com", entry.getCodebase());
-        assertEquals("Permissions should be CLIPBOARD singleton", Collections.singleton(PolicyEditorPermissions.CLIPBOARD), entry.getPermissions());
-        assertEquals("Custom permissions should be empty set", Collections.emptySet(), entry.getCustomPermissions());
-    }
-
-    @Test(expected = InvalidPolicyException.class)
-    public void testFromStringInvalid() throws Exception {
-        PolicyEntry.fromString(INVALID_POLICY_STRING);
-    }
-
-    @Test
-    public void testValidatePolicy1() throws Exception {
-        assertTrue("Valid policy should pass validation", PolicyEntry.validatePolicy(Arrays.asList(POLICY_ENTRY_STRING.split("\\r?\\n"))));
-    }
-
-    @Test
-    public void testValidatePolicy2() throws Exception {
-        assertTrue("Valid policy should pass validation", PolicyEntry.validatePolicy(POLICY_ENTRY_STRING));
-    }
-
-    @Test
-    public void testValidatePolicyInvalid1() throws Exception {
-        assertFalse("Invalid policy should not pass validation", PolicyEntry.validatePolicy(INVALID_POLICY_STRING));
-    }
-
-    @Test
-    public void testToString() {
-        final String codebase = "http://example.com";
-        final Set<PolicyEditorPermissions> permissions = Collections.singleton(PolicyEditorPermissions.CLIPBOARD);
-        final PolicyEntry entry = new PolicyEntry(codebase, permissions, Collections.<CustomPermission>emptySet());
-        assertEquals("Entry toString did not match expected output", POLICY_ENTRY_STRING, entry.toString());
-    }
-
 }
--- a/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyFileModelTest.java	Wed Jul 29 18:08:16 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyFileModelTest.java	Thu Jul 30 10:57:21 2015 -0400
@@ -39,6 +39,7 @@
 import net.sourceforge.jnlp.util.FileUtils;
 import org.junit.Before;
 import org.junit.Test;
+import sun.security.provider.PolicyParser;
 
 import java.io.File;
 import java.util.Collection;
@@ -58,6 +59,20 @@
  */
 public class PolicyFileModelTest {
 
+    private static final String EXAMPLE_CODEBASE = "http://example.com";
+    private static final String EXAMPLE_CA = "someCA";
+    private static final Collection<PolicyParser.PrincipalEntry> EMPTY_PRINCIPALS = Collections.emptyList();
+    private static final PolicyIdentifier EXAMPLE_IDENTIFIER = new PolicyIdentifier(EXAMPLE_CA, EMPTY_PRINCIPALS, EXAMPLE_CODEBASE);
+    private static final PolicyIdentifier INVALID_IDENTIFIER = new PolicyIdentifier(null, EMPTY_PRINCIPALS, "invalidURL");
+    private static final String LINEBREAK = System.getProperty("line.separator");
+
+    private static final String EXAMPLE_POLICY_1 = "grant {" + LINEBREAK
+                                                           + "\tpermission some.java.permission \"somePermission\";" + LINEBREAK
+                                                           + "};" + LINEBREAK;
+    private static final String EXAMPLE_POLICY_2 = "grant {" + LINEBREAK
+                                                           + "\tpermission some.other.java.permission \"somePermission\";" + LINEBREAK
+                                                           + "};" + LINEBREAK;
+
     private String tempFilePath;
     private PolicyFileModel model;
 
@@ -104,113 +119,99 @@
     @Test
     public void testFileHasChangedWithChange() throws Exception {
         assertFalse("Model should not report changes made initially", model.hasChanged());
-        final String codebase = "http://example.com";
-        final PolicyEditorPermissions editorPermissions = PolicyEditorPermissions.CLIPBOARD;
-        final Collection<PolicyEditorPermissions> permissions = Collections.singleton(editorPermissions);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        final PolicyEntry policyEntry = new PolicyEntry(codebase, permissions, customPermissions);
-        FileUtils.saveFile(policyEntry.toString(), new File(tempFilePath));
+        FileUtils.saveFile(EXAMPLE_POLICY_1, new File(tempFilePath));
         model.openAndParsePolicyFile();
-        final Collection<PolicyEditorPermissions> editorPermissions2 = Collections.singleton(PolicyEditorPermissions.ALL_AWT);
-        final PolicyEntry policyEntry2 = new PolicyEntry(codebase, editorPermissions2, customPermissions);
-        FileUtils.saveFile(policyEntry2.toString(), new File(tempFilePath));
+        FileUtils.saveFile(EXAMPLE_POLICY_2, new File(tempFilePath));
         assertTrue("File should be marked changed after being externally modified", model.hasChanged());
     }
 
     @Test
-    public void testAddCodebase() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        assertEquals("Should have the codebase", Collections.singleton(codebase), model.getCodebases());
+    public void testAddIdentifier() throws Exception {
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        assertEquals("Should have the identifier", Collections.singleton(EXAMPLE_IDENTIFIER), model.getIdentifiers());
     }
 
     @Test
-    public void testRemoveCodebase() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        assertEquals("Should have the codebase " + codebase, Collections.singleton(codebase), model.getCodebases());
-        model.removeCodebase(codebase);
-        assertEquals("Should not have any codebases after removed", Collections.emptySet(), model.getCodebases());
+    public void testRemoveIdentifier() throws Exception {
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        assertEquals("Should have the identifier " + EXAMPLE_IDENTIFIER, Collections.singleton(EXAMPLE_IDENTIFIER), model.getIdentifiers());
+        model.removeIdentifier(EXAMPLE_IDENTIFIER);
+        assertEquals("Should not have any identifiers after removed", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
     }
 
     @Test
     public void testClearPermissions() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
         final PolicyEditorPermissions permission = PolicyEditorPermissions.CLIPBOARD;
-        model.setPermission(codebase, permission, true);
-        assertTrue("Expected permission " + permission, model.getPermission(codebase, permission));
+        model.setPermission(EXAMPLE_IDENTIFIER, permission, true);
+        assertTrue("Expected permission " + permission, model.getPermission(EXAMPLE_IDENTIFIER, permission));
         model.clearPermissions();
-        assertFalse("Expected no permission " + permission, model.getPermission(codebase, permission));
-        assertEquals("Expected no permissions ", Collections.emptyMap(), model.getCopyOfPermissions());
+        assertFalse("Expected no permission " + permission, model.getPermission(EXAMPLE_IDENTIFIER, permission));
+        assertEquals("Expected no permissions ", Collections.<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>>emptyMap(), model.getCopyOfPermissions());
     }
 
     @Test
     public void testSettersAndGettersForPermission() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
         final PolicyEditorPermissions permission = PolicyEditorPermissions.CLIPBOARD;
-        model.setPermission(codebase, permission, true);
-        assertTrue("Expected permission " + permission, model.getPermission(codebase, permission));
+        model.setPermission(EXAMPLE_IDENTIFIER, permission, true);
+        assertTrue("Expected permission " + permission, model.getPermission(EXAMPLE_IDENTIFIER, permission));
     }
 
     @Test
     public void testClearCustomCodebase() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        model.addCustomPermissions(codebase, customPermissions);
-        assertEquals("Expected custom permission", customPermissions, model.getCopyOfCustomPermissions().get(codebase));
-        model.clearCustomCodebase(codebase);
-        assertEquals("Custom permissions were expected to be empty", Collections.emptySet(), model.getCopyOfCustomPermissions().get(codebase));
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        final PolicyParser.PermissionEntry customPermission = new CustomPolicyViewer.DisplayablePermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
+        final Collection<PolicyParser.PermissionEntry> customPermissions = Collections.singleton(customPermission);
+        model.addCustomPermissions(EXAMPLE_IDENTIFIER, customPermissions);
+        assertEquals("Expected custom permission", customPermissions, model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER));
+        model.clearCustomIdentifier(EXAMPLE_IDENTIFIER);
+        final Set<PolicyParser.PermissionEntry> result = model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER);
+        assertTrue("Custom permissions were expected to be empty, was: " + result, result.isEmpty());
     }
 
     @Test
     public void testClearCustomPermission() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        model.addCustomPermissions(codebase, customPermissions);
-        assertEquals("Expected custom permission", customPermissions, model.getCopyOfCustomPermissions().get(codebase));
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        final PolicyParser.PermissionEntry customPermission = new CustomPolicyViewer.DisplayablePermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
+        final Collection<PolicyParser.PermissionEntry> customPermissions = Collections.singleton(customPermission);
+        model.addCustomPermissions(EXAMPLE_IDENTIFIER, customPermissions);
+        assertEquals("Expected custom permission", customPermissions, model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER));
         model.clearCustomPermissions();
-        assertEquals("Custom permissions were expected to be empty", null, model.getCopyOfCustomPermissions().get(codebase));
-        assertEquals("All codebase custom permissions were expected to be empty", Collections.emptyMap(), model.getCopyOfCustomPermissions());
+        assertEquals("Custom permissions were expected to be empty", null, model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER));
+        final Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> result = model.getCopyOfCustomPermissions();
+        assertTrue("All identifier custom permissions were expected to be empty, was: " + result, result.isEmpty());
     }
 
     @Test
     public void testAddCustomPermissions() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        final CustomPermission customPermission = new CustomPermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
-        final Collection<CustomPermission> customPermissions = Collections.singleton(customPermission);
-        model.addCustomPermissions(codebase, customPermissions);
-        assertEquals("Expected file/user home/all-actions permission ", customPermissions, model.getCopyOfCustomPermissions().get(codebase));
-        final CustomPermission customPermission2 = new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY);
-        final Collection<CustomPermission> customPermissions2 = Collections.singleton(customPermission2);
-        model.addCustomPermissions(codebase, customPermissions2);
-        assertTrue("Expected audio play permission ", model.getCopyOfCustomPermissions().get(codebase).contains(customPermission2));
-        final HashSet<CustomPermission> customPermissionHashSet = new HashSet<>();
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        final PolicyParser.PermissionEntry customPermission = new CustomPolicyViewer.DisplayablePermission(PermissionType.FILE_PERMISSION, PermissionTarget.USER_HOME, PermissionActions.FILE_ALL);
+        final Collection<PolicyParser.PermissionEntry> customPermissions = Collections.singleton(customPermission);
+        model.addCustomPermissions(EXAMPLE_IDENTIFIER, customPermissions);
+        assertEquals("Expected file/user home/all-actions permission ", customPermissions, model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER));
+        final CustomPolicyViewer.DisplayablePermission customPermission2 = new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY);
+        final Collection<CustomPolicyViewer.DisplayablePermission> customPermissions2 = Collections.singleton(customPermission2);
+        model.addCustomPermissions(EXAMPLE_IDENTIFIER, customPermissions2);
+        assertTrue("Expected audio play permission ", model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER).contains(customPermission2));
+        final HashSet<PolicyParser.PermissionEntry> customPermissionHashSet = new HashSet<>();
         customPermissionHashSet.add(customPermission);
         customPermissionHashSet.add(customPermission2);
-        assertEquals("Expected custom permission ", customPermissionHashSet, model.getCopyOfCustomPermissions().get(codebase));
+        assertEquals("Expected custom permission ", customPermissionHashSet, model.getCopyOfCustomPermissions().get(EXAMPLE_IDENTIFIER));
     }
 
     @Test
     public void testAllPermissionsAreFalseInitially() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        final Map<PolicyEditorPermissions, Boolean> policyEditorPermissions = model.getCopyOfPermissions().get(codebase);
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        final Map<PolicyEditorPermissions, Boolean> policyEditorPermissions = model.getCopyOfPermissions().get(EXAMPLE_IDENTIFIER);
         for (final Map.Entry<PolicyEditorPermissions, Boolean> entry : policyEditorPermissions.entrySet()) {
             assertFalse("Expected " + entry.getKey() + " to be false", entry.getValue());
         }
@@ -218,10 +219,9 @@
 
     @Test
     public void testAllPermissionsAreInitialized() throws Exception {
-        assertEquals("Should not have any codebases initially", Collections.emptySet(), model.getCodebases());
-        final String codebase = "http://example.com";
-        model.addCodebase(codebase);
-        final Map<PolicyEditorPermissions, Boolean> policyEditorPermissions = model.getCopyOfPermissions().get(codebase);
+        assertEquals("Should not have any identifiers initially", Collections.<PolicyIdentifier>emptySet(), model.getIdentifiers());
+        model.addIdentifier(EXAMPLE_IDENTIFIER);
+        final Map<PolicyEditorPermissions, Boolean> policyEditorPermissions = model.getCopyOfPermissions().get(EXAMPLE_IDENTIFIER);
         for (final PolicyEditorPermissions perm : PolicyEditorPermissions.values()) {
             assertTrue(perm + " should have been present as a key", policyEditorPermissions.containsKey(perm));
         }
@@ -229,21 +229,21 @@
 
     @Test
     public void testGetCopyOfPermissionsIsCopy() throws Exception {
-        final Map<String, Map<PolicyEditorPermissions, Boolean>> codebasePermissionsMap = model.getCopyOfPermissions();
-        assertEquals("Map should be initially empty", Collections.emptyMap(), codebasePermissionsMap);
-        codebasePermissionsMap.put("invalid codebase", Collections.singletonMap(PolicyEditorPermissions.CLIPBOARD, true));
-        final Map<String, Map<PolicyEditorPermissions, Boolean>> codebasePermissionsMap2 = model.getCopyOfPermissions();
-        assertEquals("New copy should be initially empty", Collections.emptyMap(), codebasePermissionsMap2);
+        final Map<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>> codebasePermissionsMap = model.getCopyOfPermissions();
+        assertEquals("Map should be initially empty", Collections.<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>>emptyMap(), codebasePermissionsMap);
+        codebasePermissionsMap.put(INVALID_IDENTIFIER, Collections.singletonMap(PolicyEditorPermissions.CLIPBOARD, true));
+        final Map<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>> codebasePermissionsMap2 = model.getCopyOfPermissions();
+        assertEquals("New copy should be initially empty", Collections.<PolicyIdentifier, Map<PolicyEditorPermissions, Boolean>>emptyMap(), codebasePermissionsMap2);
         assertNotEquals("Modified map should not equal newly copied map", codebasePermissionsMap, codebasePermissionsMap2);
     }
 
     @Test
     public void testGetCopyOfCustomPermissionsIsCopy() throws Exception {
-        final Map<String, Set<CustomPermission>> codebasePermissionsMap = model.getCopyOfCustomPermissions();
-        assertEquals("Map should be initially empty", Collections.emptyMap(), codebasePermissionsMap);
-        codebasePermissionsMap.put("invalid codebase", Collections.singleton(new CustomPermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY)));
-        final Map<String, Set<CustomPermission>> codebasePermissionsMap2 = model.getCopyOfCustomPermissions();
-        assertEquals("New copy should be initially empty", Collections.emptyMap(), codebasePermissionsMap2);
+        final Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> codebasePermissionsMap = model.getCopyOfCustomPermissions();
+        assertEquals("Map should be initially empty", Collections.<PolicyIdentifier, Set<PolicyParser.PermissionEntry>>emptyMap(), codebasePermissionsMap);
+        codebasePermissionsMap.put(INVALID_IDENTIFIER, Collections.singleton((PolicyParser.PermissionEntry) new CustomPolicyViewer.DisplayablePermission(PermissionType.AUDIO_PERMISSION, PermissionTarget.PLAY)));
+        final Map<PolicyIdentifier, Set<PolicyParser.PermissionEntry>> codebasePermissionsMap2 = model.getCopyOfCustomPermissions();
+        assertEquals("New copy should be initially empty", Collections.<PolicyIdentifier, Set<PolicyParser.PermissionEntry>>emptyMap(), codebasePermissionsMap2);
         assertNotEquals("Modified set should not equal newly copied set", codebasePermissionsMap, codebasePermissionsMap2);
     }
 }