You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ch...@apache.org on 2008/08/02 11:56:04 UTC
svn commit: r681945 [1/9] - in /felix/trunk/org.osgi.compendium: ./ doc/
src/main/java/info/ src/main/java/info/dmtree/
src/main/java/info/dmtree/notification/
src/main/java/info/dmtree/notification/spi/
src/main/java/info/dmtree/registry/ src/main/jav...
Author: christian
Date: Sat Aug 2 02:56:01 2008
New Revision: 681945
URL: http://svn.apache.org/viewvc?rev=681945&view=rev
Log:
FELIX-514 Updated compendium bundle to R4.1 OSGi API
Added:
felix/trunk/org.osgi.compendium/src/main/java/info/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Acl.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtEvent.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtEventListener.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtException.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtIllegalStateException.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtSession.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/MetaNode.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Uri.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/notification/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/notification/AlertItem.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/notification/NotificationService.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/notification/spi/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/notification/spi/RemoteAlertSender.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/registry/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/registry/DmtServiceFactory.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/AlertPermission.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPermission.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/security/DmtPrincipalPermission.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/DataPlugin.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ExecPlugin.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadWriteDataSession.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/ReadableDataSession.java
felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/spi/TransactionalDataSession.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/application/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationContext.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceEvent.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/application/ApplicationServiceListener.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/application/Framework.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationAdminPermission.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationDescriptor.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationException.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/ApplicationHandle.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/application/ScheduledApplication.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/BundleInfo.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdmin.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentAdminPermission.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentException.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/DeploymentPackage.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentCustomizerPermission.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/DeploymentSession.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessor.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/deploymentadmin/spi/ResourceProcessorException.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorAdmin.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorListener.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/Monitorable.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitoringJob.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/monitor/StatusVariable.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/gsm/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMEICondition.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/gsm/IMSICondition.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java
felix/trunk/org.osgi.compendium/src/main/resources/info/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/spi/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/spi/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/notification/spi/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/registry/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/registry/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/registry/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/security/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/security/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/security/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/spi/
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/spi/package.html
felix/trunk/org.osgi.compendium/src/main/resources/info/dmtree/spi/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/application/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/application/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/application/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/application/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/application/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/application/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/spi/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/spi/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/deploymentadmin/spi/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/monitor/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/monitor/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/service/monitor/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/gsm/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/gsm/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/gsm/packageinfo
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/mobile/
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/mobile/package.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/mobile/packageinfo
felix/trunk/org.osgi.compendium/xmlns/app/
felix/trunk/org.osgi.compendium/xmlns/app/app.xsd
Modified:
felix/trunk/org.osgi.compendium/doc/changelog.txt
felix/trunk/org.osgi.compendium/pom.xml
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/cm/ConfigurationAdmin.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/service/useradmin/Authorization.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTracker.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/xml/XMLParserActivator.java
felix/trunk/org.osgi.compendium/src/main/resources/about.html
felix/trunk/org.osgi.compendium/src/main/resources/org/osgi/util/tracker/packageinfo
felix/trunk/org.osgi.compendium/xmlns/scr/scr.xsd
Modified: felix/trunk/org.osgi.compendium/doc/changelog.txt
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/doc/changelog.txt?rev=681945&r1=681944&r2=681945&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/doc/changelog.txt (original)
+++ felix/trunk/org.osgi.compendium/doc/changelog.txt Sat Aug 2 02:56:01 2008
@@ -1,3 +1,5 @@
+* [2008-08-02] Updated from Compendium 4.0 to 4.1 (FELIX-514)
+
Changes from 1.0.0 to 1.0.1
-------------------------------------
Modified: felix/trunk/org.osgi.compendium/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/pom.xml?rev=681945&r1=681944&r2=681945&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/pom.xml (original)
+++ felix/trunk/org.osgi.compendium/pom.xml Sat Aug 2 02:56:01 2008
@@ -27,10 +27,10 @@
<url>http://www.osgi.org/</url>
</organization>
<modelVersion>4.0.0</modelVersion>
- <name>OSGi R4 Compendium Bundle</name>
<description>OSGi Service Platform Release 4 Compendium Interfaces and Classes.</description>
<artifactId>org.osgi.compendium</artifactId>
<version>1.1.0-SNAPSHOT</version>
+ <name>OSGi R4 Compendium Bundle</name>
<packaging>bundle</packaging>
<dependencies>
<dependency>
@@ -58,13 +58,13 @@
<extensions>true</extensions>
<configuration>
<instructions>
+ <Bundle-Version>4.1.0</Bundle-Version>
<Bundle-SymbolicName>org.osgi.compendium</Bundle-SymbolicName>
- <Export-Package>org.osgi.service.cm,org.osgi.service.component,org.osgi.service.device,org.osgi.service.event,org.osgi.service.http,org.osgi.service.io,org.osgi.service.log,org.osgi.service.metatype,org.osgi.service.prefs,org.osgi.service.provisioning,org.osgi.service.upnp,org.osgi.service.useradmin,org.osgi.service.wireadmin,org.osgi.util.measurement,org.osgi.util.position,org.osgi.util.tracker,org.osgi.util.xml</Export-Package>
+ <Bundle-Copyright>Copyright (c) OSGi Alliance (2000, 2007). All Rights Reserved.</Bundle-Copyright>
+ <Bundle-Category>osgi</Bundle-Category>
+ <Export-Package>info.dmtree.notification,info.dmtree.notification.spi,info.dmtree.registry,info.dmtree.security,info.dmtree.spi,org.osgi.service.application,org.osgi.service.cm,org.osgi.service.component,org.osgi.service.deploymentadmin,org.osgi.service.deploymentadmin.spi,org.osgi.service.device,org.osgi.service.event,org.osgi.service.http,org.osgi.service.io,org.osgi.service.log,org.osgi.service.metatype,org.osgi.service.monitor,org.osgi.service.prefs,org.osgi.service.provisioning,org.osgi.service.upnp,org.osgi.service.useradmin,org.osgi.service.wireadmin,org.osgi.util.gsm,org.osgi.util.measurement,org.osgi.util.mobile,org.osgi.util.position,org.osgi.util.tracker,org.osgi.util.xml</Export-Package>
<Import-Package />
<DynamicImport-Package>*</DynamicImport-Package>
- <Bundle-Version>4</Bundle-Version>
- <Bundle-Copyright>Copyright (c) OSGi Alliance (2000, 2005). All Rights Reserved.</Bundle-Copyright>
- <Bundle-Category>osgi</Bundle-Category>
</instructions>
</configuration>
</plugin>
Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Acl.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Acl.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Acl.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/Acl.java Sat Aug 2 02:56:01 2008
@@ -0,0 +1,576 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/Acl.java,v 1.6 2006/07/12 21:21:37 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package info.dmtree;
+
+import java.util.*;
+
+/**
+ * <code>Acl</code> is an immutable class representing structured access to
+ * DMT ACLs. Under OMA DM the ACLs are defined as strings with an internal
+ * syntax.
+ * <p>
+ * The methods of this class taking a principal as parameter accept remote
+ * server IDs (as passed to
+ * {@link DmtAdmin#getSession(String, String, int) DmtAdmin.getSession}), as
+ * well as " <code>*</code> " indicating any principal.
+ * <p>
+ * The syntax for valid remote server IDs:<br>
+ * <<i>server-identifier</i>> ::= All printable characters except
+ * <code>'='</code>, <code>'&'</code>, <code>'*'</code>, <code>'+'</code>
+ * or white-space characters.
+ */
+public final class Acl {
+
+ // ----- Public constants -----//
+
+ /**
+ * Principals holding this permission can issue GET command on the node
+ * having this ACL.
+ */
+ public static final int GET = 1;
+
+ /**
+ * Principals holding this permission can issue ADD commands on the node
+ * having this ACL.
+ */
+ public static final int ADD = 2;
+
+ /**
+ * Principals holding this permission can issue REPLACE commands on the node
+ * having this ACL.
+ */
+ public static final int REPLACE = 4;
+
+ /**
+ * Principals holding this permission can issue DELETE commands on the node
+ * having this ACL.
+ */
+ public static final int DELETE = 8;
+
+ /**
+ * Principals holding this permission can issue EXEC commands on the node
+ * having this ACL.
+ */
+ public static final int EXEC = 16;
+
+ /**
+ * Principals holding this permission can issue any command on the node
+ * having this ACL. This permission is the logical OR of {@link #ADD},
+ * {@link #DELETE}, {@link #EXEC}, {@link #GET} and {@link #REPLACE}
+ * permissions.
+ */
+ public static final int ALL_PERMISSION = ADD | DELETE | EXEC | GET
+ | REPLACE;
+
+ // ----- Private constants -----//
+
+ private static final int[] PERMISSION_CODES = new int[] { ADD, DELETE,
+ EXEC, GET, REPLACE };
+
+ private static final String[] PERMISSION_NAMES = new String[] { "Add",
+ "Delete", "Exec", "Get", "Replace" };
+
+ private static final String ALL_PRINCIPALS = "*";
+
+ // ----- Private fields -----//
+
+ // the implementation takes advantage of this being a sorted map
+ private final TreeMap principalPermissions;
+
+ private final int globalPermissions;
+
+ // ----- Public constructors -----//
+
+ /**
+ * Create an instance of the ACL from its canonic string representation.
+ *
+ * @param acl The string representation of the ACL as defined in OMA DM. If
+ * <code>null</code> or empty then it represents an empty list of
+ * principals with no permissions.
+ * @throws IllegalArgumentException if acl is not a valid OMA DM ACL string
+ */
+ public Acl(String acl) {
+ if (acl == null || acl.equals("")) { // empty permission set
+ principalPermissions = new TreeMap();
+ globalPermissions = 0;
+ return;
+ }
+
+ TreeMap tempPrincipalPermissions = new TreeMap();
+ int tempGlobalPermissions = 0;
+
+ String[] aclEntries = split(acl, '&', -1);
+ for (int i = 0; i < aclEntries.length; i++) {
+ if (aclEntries[i].length() == 0)
+ throw new IllegalArgumentException(
+ "Invalid ACL string: empty ACL entry.");
+
+ String[] entryParts = split(aclEntries[i], '=', 2);
+ if (entryParts.length == 1)
+ throw new IllegalArgumentException(
+ "Invalid ACL string: no '=' in ACL entry.");
+ if (entryParts[1].length() == 0)
+ throw new IllegalArgumentException(
+ "Invalid ACL string: no server identifiers in ACL entry.");
+
+ int command = parseCommand(entryParts[0]);
+ String[] serverIds = split(entryParts[1], '+', -1);
+ for (int j = 0; j < serverIds.length; j++) {
+ if (serverIds[j].length() == 0)
+ throw new IllegalArgumentException(
+ "Invalid ACL string: empty server identifier.");
+
+ if (serverIds[j].equals(ALL_PRINCIPALS))
+ tempGlobalPermissions |= command;
+ else {
+ checkServerId(serverIds[j], "Invalid ACL string: "
+ + "server ID contains illegal character");
+ Integer n = (Integer) tempPrincipalPermissions
+ .get(serverIds[j]);
+ int oldPermission = (n != null) ? n.intValue() : 0;
+ tempPrincipalPermissions.put(serverIds[j], new Integer(
+ oldPermission | command));
+ }
+ }
+ }
+
+ principalPermissions = tempPrincipalPermissions;
+ globalPermissions = tempGlobalPermissions;
+ }
+
+ /**
+ * Creates an instance with a specified list of principals and the
+ * permissions they hold. The two arrays run in parallel, that is
+ * <code>principals[i]</code> will hold <code>permissions[i]</code> in
+ * the ACL.
+ * <p>
+ * A principal name may not appear multiple times in the 'principals'
+ * argument. If the "*" principal appears in the array, the
+ * corresponding permissions will be granted to all principals (regardless
+ * of whether they appear in the array or not).
+ *
+ * @param principals The array of principals
+ * @param permissions The array of permissions
+ * @throws IllegalArgumentException if the length of the two arrays are not
+ * the same, if any array element is invalid, or if a principal
+ * appears multiple times in the <code>principals</code> array
+ */
+ public Acl(String[] principals, int[] permissions) {
+ if (principals.length != permissions.length)
+ throw new IllegalArgumentException(
+ "The lengths of the principal and permission arrays are not the same.");
+
+ TreeMap tempPrincipalPermissions = new TreeMap();
+ int tempGlobalPermissions = 0;
+
+ for (int i = 0; i < principals.length; i++) {
+ // allow one * in 'principals' array, remove after loop
+ if (!ALL_PRINCIPALS.equals(principals[i]))
+ checkPrincipal(principals[i]);
+ checkPermissions(permissions[i]);
+
+ Integer permInt = new Integer(permissions[i]);
+ Object old = tempPrincipalPermissions.put(principals[i], permInt);
+ if (old != null)
+ throw new IllegalArgumentException("Principal '"
+ + principals[i]
+ + "' appears multiple times in the principal array.");
+ }
+
+ // set the global permissions if there was a * in the array
+ Object globalPermObj = tempPrincipalPermissions.remove(ALL_PRINCIPALS);
+ if (globalPermObj != null)
+ tempGlobalPermissions = ((Integer) globalPermObj).intValue();
+
+ principalPermissions = tempPrincipalPermissions;
+ globalPermissions = tempGlobalPermissions;
+ }
+
+ // ----- Private constructors -----//
+
+ /**
+ * Creates an instance identical to the <code>base</code> ACL except for
+ * the permissions of the given <code>principal</code>, which are
+ * overwritten with the given <code>permissions</code>.
+ * <p>
+ * Assumes that the permissions parameter has been checked. All
+ * modifications of an <code>Acl</code> (add, delete, set) are done
+ * through this method.
+ *
+ * @param base The ACL that provides all permissions except for permissions
+ * of the given principal.
+ * @param principal The entity to which permission should be granted.
+ * @param permissions The set of permissions to be given. The parameter can
+ * be a logical <code>or</code> of the permission constants defined
+ * in this class.
+ */
+ private Acl(Acl base, String principal, int permissions) {
+ // make a shallow copy of the permission table, the keys (String) and
+ // values (Integer) are immutable anyway
+ TreeMap tempPrincipalPermissions = (TreeMap) base.principalPermissions
+ .clone();
+ int tempGlobalPermissions = base.globalPermissions;
+
+ int deletedGlobalPerm = tempGlobalPermissions & ~permissions;
+ if (ALL_PRINCIPALS.equals(principal)) {
+ deleteFromAll(tempPrincipalPermissions, deletedGlobalPerm);
+ tempGlobalPermissions = permissions;
+ } else {
+ checkPrincipal(principal);
+
+ if (deletedGlobalPerm != 0)
+ throw new IllegalArgumentException(
+ "Cannot revoke globally set permissions ("
+ + writeCommands(deletedGlobalPerm)
+ + ") from a specific principal (" + principal
+ + ").");
+
+ setPrincipalPermission(tempPrincipalPermissions, principal,
+ permissions);
+ }
+
+ principalPermissions = tempPrincipalPermissions;
+ globalPermissions = tempGlobalPermissions;
+ }
+
+ // ----- Public methods -----//
+
+ /**
+ * Checks whether the given object is equal to this <code>Acl</code>
+ * instance. Two <code>Acl</code> instances are equal if they allow the
+ * same set of permissions for the same set of principals.
+ *
+ * @param obj the object to compare with this <code>Acl</code> instance
+ * @return <code>true</code> if the parameter represents the same ACL as
+ * this instance
+ */
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if (!(obj instanceof Acl))
+ return false;
+
+ Acl other = (Acl) obj;
+
+ if (globalPermissions != other.globalPermissions
+ || principalPermissions.size() != other.principalPermissions
+ .size())
+ return false;
+
+ // principalPermissions sets cannot be easily compared, because they are
+ // not canonical: the global permissions may or may not be present for
+ // each principal, without changing the meaning of the Acl object.
+
+ // Compare canonical string representations, inefficient but simple.
+ return toString().equals(other.toString());
+ }
+
+ /**
+ * Returns the hash code for this ACL instance. If two <code>Acl</code>
+ * instances are equal according to the {@link #equals} method, then calling
+ * this method on each of them must produce the same integer result.
+ *
+ * @return hash code for this ACL
+ */
+ public int hashcode() {
+ // Using the hash code of the canonical string representation, because
+ // the principalPermissions set is not canonical (see above).
+ return toString().hashCode();
+ }
+
+ /**
+ * Create a new <code>Acl</code> instance from this <code>Acl</code> with
+ * the given permission added for the given principal. The already existing
+ * permissions of the principal are not affected.
+ *
+ * @param principal The entity to which permissions should be granted, or
+ * "*" to grant permissions to all principals.
+ * @param permissions The permissions to be given. The parameter can be a
+ * logical <code>or</code> of more permission constants defined in
+ * this class.
+ * @return a new <code>Acl</code> instance
+ * @throws IllegalArgumentException if <code>principal</code> is not a
+ * valid principal name or if <code>permissions</code> is not a
+ * valid combination of the permission constants defined in this
+ * class
+ */
+ public synchronized Acl addPermission(String principal, int permissions) {
+ checkPermissions(permissions);
+
+ int oldPermissions = getPermissions(principal);
+ return setPermission(principal, oldPermissions | permissions);
+ }
+
+ /**
+ * Create a new <code>Acl</code> instance from this <code>Acl</code> with
+ * the given permission revoked from the given principal. Other permissions
+ * of the principal are not affected.
+ * <p>
+ * Note, that it is not valid to revoke a permission from a specific
+ * principal if that permission is granted globally to all principals.
+ *
+ * @param principal The entity from which permissions should be revoked, or
+ * "*" to revoke permissions from all principals.
+ * @param permissions The permissions to be revoked. The parameter can be a
+ * logical <code>or</code> of more permission constants defined in
+ * this class.
+ * @return a new <code>Acl</code> instance
+ * @throws IllegalArgumentException if <code>principal</code> is not a
+ * valid principal name, if <code>permissions</code> is not a
+ * valid combination of the permission constants defined in this
+ * class, or if a globally granted permission would have been
+ * revoked from a specific principal
+ */
+ public synchronized Acl deletePermission(String principal, int permissions) {
+ checkPermissions(permissions);
+
+ int oldPermissions = getPermissions(principal);
+ return setPermission(principal, oldPermissions & ~permissions);
+ }
+
+ /**
+ * Get the permissions associated to a given principal.
+ *
+ * @param principal The entity whose permissions to query, or "*"
+ * to query the permissions that are granted globally, to all
+ * principals
+ * @return The permissions of the given principal. The returned
+ * <code>int</code> is a bitmask of the permission constants defined
+ * in this class
+ * @throws IllegalArgumentException if <code>principal</code> is not a
+ * valid principal name
+ */
+ public synchronized int getPermissions(String principal) {
+ int permissions = 0;
+
+ if (!(ALL_PRINCIPALS.equals(principal))) {
+ checkPrincipal(principal);
+ Object po = principalPermissions.get(principal);
+ if (po != null)
+ permissions = ((Integer) po).intValue();
+ }
+
+ return permissions | globalPermissions;
+ }
+
+ /**
+ * Check whether the given permissions are granted to a certain principal.
+ * The requested permissions are specified as a bitfield, for example
+ * <code>(Acl.ADD | Acl.DELETE | Acl.GET)</code>.
+ *
+ * @param principal The entity to check, or "*" to check whether
+ * the given permissions are granted to all principals globally
+ * @param permissions The permissions to check
+ * @return <code>true</code> if the principal holds all the given permissions
+ * @throws IllegalArgumentException if <code>principal</code> is not a
+ * valid principal name or if <code>permissions</code> is not a
+ * valid combination of the permission constants defined in this
+ * class
+ */
+ public synchronized boolean isPermitted(String principal, int permissions) {
+ checkPermissions(permissions);
+
+ int hasPermissions = getPermissions(principal);
+ return (permissions & hasPermissions) == permissions;
+ }
+
+ /**
+ * Create a new <code>Acl</code> instance from this <code>Acl</code> where
+ * all permissions for the given principal are overwritten with the given
+ * permissions.
+ * <p>
+ * Note, that when changing the permissions of a specific principal, it is
+ * not allowed to specify a set of permissions stricter than the global set
+ * of permissions (that apply to all principals).
+ *
+ * @param principal The entity to which permissions should be granted, or
+ * "*" to globally grant permissions to all principals.
+ * @param permissions The set of permissions to be given. The parameter is
+ * a bitmask of the permission constants defined in this class.
+ * @return a new <code>Acl</code> instance
+ * @throws IllegalArgumentException if <code>principal</code> is not a
+ * valid principal name, if <code>permissions</code> is not a
+ * valid combination of the permission constants defined in this
+ * class, or if a globally granted permission would have been
+ * revoked from a specific principal
+ */
+ public synchronized Acl setPermission(String principal, int permissions) {
+ checkPermissions(permissions);
+
+ Acl newPermission = new Acl(this, principal, permissions);
+ return newPermission;
+ }
+
+ /**
+ * Get the list of principals who have any kind of permissions on this node.
+ * The list only includes those principals that have been explicitly
+ * assigned permissions (so "*" is never returned), globally set
+ * permissions naturally apply to all other principals as well.
+ *
+ * @return The array of principals having permissions on this node.
+ */
+ public String[] getPrincipals() {
+ return (String[]) (principalPermissions.keySet().toArray(new String[0]));
+ }
+
+ /**
+ * Give the canonic string representation of this ACL. The operations are in
+ * the following order: {Add, Delete, Exec, Get, Replace}, principal names
+ * are sorted alphabetically.
+ *
+ * @return The string representation as defined in OMA DM.
+ */
+ public synchronized String toString() {
+ String acl = null;
+ for (int i = 0; i < PERMISSION_CODES.length; i++)
+ acl = writeEntry(PERMISSION_CODES[i], acl);
+
+ return (acl != null) ? acl : "";
+ }
+
+ // ----- Private utility methods -----//
+
+ private String writeEntry(int command, String acl) {
+ String aclEntry = null;
+
+ if ((command & globalPermissions) > 0)
+ aclEntry = ALL_PRINCIPALS;
+ else {
+ // TreeMap guarantees alphabetical ordering of keys during traversal
+ Iterator i = principalPermissions.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry) i.next();
+ if ((command & ((Integer) entry.getValue()).intValue()) > 0)
+ aclEntry = appendEntry(aclEntry, '+', (String) entry
+ .getKey());
+ }
+ }
+
+ if (aclEntry == null)
+ return acl;
+
+ return appendEntry(acl, '&', writeCommands(command) + '=' + aclEntry);
+ }
+
+ private static void deleteFromAll(TreeMap principalPermissions, int perm) {
+ Iterator i = principalPermissions.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry) i.next();
+ setPrincipalPermission(principalPermissions, (String) entry
+ .getKey(), ((Integer) entry.getValue()).intValue() & ~perm);
+ }
+ }
+
+ private static void setPrincipalPermission(TreeMap principalPermissions,
+ String principal, int perm) {
+ if (perm == 0)
+ principalPermissions.remove(principal);
+ else
+ principalPermissions.put(principal, new Integer(perm));
+ }
+
+ private static String writeCommands(int command) {
+ String commandStr = null;
+ for (int i = 0; i < PERMISSION_CODES.length; i++)
+ if ((command & PERMISSION_CODES[i]) != 0)
+ commandStr = appendEntry(commandStr, ',', PERMISSION_NAMES[i]);
+
+ return (commandStr != null) ? commandStr : "";
+ }
+
+ private static String appendEntry(String base, char separator, String entry) {
+ return (base != null) ? base + separator + entry : entry;
+ }
+
+ private static int parseCommand(String command) {
+ int i = Arrays.asList(PERMISSION_NAMES).indexOf(command);
+ if (i == -1)
+ throw new IllegalArgumentException(
+ "Invalid ACL string: unknown command '" + command + "'.");
+
+ return PERMISSION_CODES[i];
+ }
+
+ private static void checkPermissions(int perm) {
+ if ((perm & ~ALL_PERMISSION) != 0)
+ throw new IllegalArgumentException("Invalid ACL permission value: "
+ + perm);
+ }
+
+ private static void checkPrincipal(String principal) {
+ if (principal == null)
+ throw new IllegalArgumentException("Principal is null.");
+
+ checkServerId(principal, "Principal name contains illegal character");
+ }
+
+ private static void checkServerId(String serverId, String errorText) {
+ char[] chars = serverId.toCharArray();
+ for (int i = 0; i < chars.length; i++)
+ if ("*=+&".indexOf(chars[i]) != -1
+ || Character.isWhitespace(chars[i]))
+ throw new IllegalArgumentException(errorText + " '" + chars[i]
+ + "'.");
+ }
+
+ private static String[] split(String input, char sep, int limit) {
+ Vector v = new Vector();
+ boolean limited = (limit > 0);
+ int applied = 0;
+ int index = 0;
+ StringBuffer part = new StringBuffer();
+
+ while (index < input.length()) {
+ char ch = input.charAt(index);
+ if (ch != sep)
+ part.append(ch);
+ else {
+ ++applied;
+ v.add(part.toString());
+ part = new StringBuffer();
+ }
+ ++index;
+ if (limited && applied == limit - 1)
+ break;
+ }
+ while (index < input.length()) {
+ char ch = input.charAt(index);
+ part.append(ch);
+ ++index;
+ }
+ v.add(part.toString());
+
+ int last = v.size();
+ if (0 == limit) {
+ for (int j = v.size() - 1; j >= 0; --j) {
+ String s = (String) v.elementAt(j);
+ if ("".equals(s))
+ --last;
+ else
+ break;
+ }
+ }
+
+ String[] ret = new String[last];
+ for (int i = 0; i < last; ++i)
+ ret[i] = (String) v.elementAt(i);
+
+ return ret;
+ }
+}
Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtAdmin.java Sat Aug 2 02:56:01 2008
@@ -0,0 +1,281 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtAdmin.java,v 1.9 2006/07/11 16:59:41 tszeredi Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package info.dmtree;
+
+/**
+ * An interface providing methods to open sessions and register listeners. The
+ * implementation of <code>DmtAdmin</code> should register itself in the OSGi
+ * service registry as a service. <code>DmtAdmin</code> is the entry point for
+ * applications to use the DMT API.
+ * <p>
+ * The <code>getSession</code> methods are used to open a session on a
+ * specified subtree of the DMT. A typical way of usage:
+ * <pre>
+ * serviceRef = context.getServiceReference(DmtAdmin.class.getName());
+ * DmtAdmin admin = (DmtAdmin) context.getService(serviceRef);
+ * DmtSession session = admin.getSession("./OSGi/Configuration");
+ * session.createInteriorNode("./OSGi/Configuration/my.table");
+ * </pre>
+ * <p>
+ * The methods for opening a session take a node URI (the session root) as a
+ * parameter. All segments of the given URI must be within the segment length
+ * limit of the implementation, and the special characters '/' and '\' must be
+ * escaped (preceded by a '\'). Any string can be converted to a valid URI
+ * segment using the {@link Uri#mangle(String)} method.
+ * <p>
+ * It is possible to specify a lock mode when opening the session (see lock type
+ * constants in {@link DmtSession}). This determines whether the session can
+ * run in parallel with other sessions, and the kinds of operations that can be
+ * performed in the session. All Management Objects constituting the device
+ * management tree must support read operations on their nodes, while support
+ * for write operations depends on the Management Object. Management Objects
+ * supporting write access may support transactional write, non-transactional
+ * write or both. Users of <code>DmtAdmin</code> should consult the Management
+ * Object specification and implementation for the supported update modes. If
+ * Management Object definition permits, implementations are encouraged to
+ * support both update modes.
+ * <p>
+ * This interface also contains methods for manipulating the set of
+ * <code>DmtEventListener</code> objects that are called when the structure or
+ * content of the tree is changed. These methods are not needed in an OSGi
+ * environment, clients should register listeners through the Event Admin
+ * service.
+ */
+public interface DmtAdmin {
+ /**
+ * Opens a <code>DmtSession</code> for local usage on a given subtree of
+ * the DMT with non transactional write lock. This call is equivalent to the
+ * following:
+ * <code>getSession(null, subtreeUri, DmtSession.LOCK_TYPE_EXCLUSIVE)</code>
+ * <p>
+ * The <code>subtreeUri</code> parameter must contain an absolute URI. It
+ * can also be <code>null</code>, in this case the session is opened with
+ * the default session root, ".", that gives access to the whole
+ * tree.
+ * <p>
+ * To perform this operation the caller must have <code>DmtPermission</code>
+ * for the <code>subtreeUri</code> node with the Get action present.
+ *
+ * @param subtreeUri the subtree on which DMT manipulations can be performed
+ * within the returned session
+ * @return a <code>DmtSession</code> object for the requested subtree
+ * @throws DmtException with the following possible error codes:
+ * <ul>
+ * <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+ * a segment of it is too long, or if it has too many segments
+ * <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+ * syntactically invalid
+ * <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+ * specifies a non-existing node
+ * <li><code>SESSION_CREATION_TIMEOUT</code> if the operation
+ * timed out because of another ongoing session
+ * <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+ * specifies a relative URI, or some unspecified error is
+ * encountered while attempting to complete the command
+ * </ul>
+ * @throws SecurityException if the caller does not have
+ * <code>DmtPermission</code> for the given root node with the Get
+ * action present
+ */
+ DmtSession getSession(String subtreeUri) throws DmtException;
+
+ /**
+ * Opens a <code>DmtSession</code> for local usage on a specific DMT
+ * subtree with a given lock mode. This call is equivalent to the
+ * following: <code>getSession(null, subtreeUri, lockMode)</code>
+ * <p>
+ * The <code>subtreeUri</code> parameter must contain an absolute URI. It
+ * can also be <code>null</code>, in this case the session is opened with
+ * the default session root, ".", that gives access to the whole
+ * tree.
+ * <p>
+ * To perform this operation the caller must have <code>DmtPermission</code>
+ * for the <code>subtreeUri</code> node with the Get action present.
+ *
+ * @param subtreeUri the subtree on which DMT manipulations can be performed
+ * within the returned session
+ * @param lockMode one of the lock modes specified in
+ * <code>DmtSession</code>
+ * @return a <code>DmtSession</code> object for the requested subtree
+ * @throws DmtException with the following possible error codes:
+ * <ul>
+ * <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+ * a segment of it is too long, or if it has too many segments
+ * <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+ * syntactically invalid
+ * <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+ * specifies a non-existing node
+ * <li><code>FEATURE_NOT_SUPPORTED</code> if atomic sessions are
+ * not supported by the implementation and <code>lockMode</code>
+ * requests an atomic session
+ * <li><code>SESSION_CREATION_TIMEOUT</code> if the operation
+ * timed out because of another ongoing session
+ * <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+ * specifies a relative URI, if <code>lockMode</code> is unknown,
+ * or some unspecified error is encountered while attempting to
+ * complete the command
+ * </ul>
+ * @throws SecurityException if the caller does not have
+ * <code>DmtPermission</code> for the given root node with the Get
+ * action present
+ */
+ DmtSession getSession(String subtreeUri, int lockMode) throws DmtException;
+
+ /**
+ * Opens a <code>DmtSession</code> on a specific DMT subtree using a
+ * specific lock mode on behalf of a remote principal. If local management
+ * applications are using this method then they should provide
+ * <code>null</code> as the first parameter. Alternatively they can use
+ * other forms of this method without providing a principal string.
+ * <p>
+ * The <code>subtreeUri</code> parameter must contain an absolute URI. It
+ * can also be <code>null</code>, in this case the session is opened with
+ * the default session root, ".", that gives access to the whole
+ * tree.
+ * <p>
+ * This method is guarded by <code>DmtPrincipalPermission</code> in case of
+ * remote sessions. In addition, the caller must have Get access rights
+ * (ACL in case of remote sessions, <code>DmtPermission</code> in case of
+ * local sessions) on the <code>subtreeUri</code> node to perform this
+ * operation.
+ *
+ * @param principal the identifier of the remote server on whose behalf the
+ * data manipulation is performed, or <code>null</code> for local
+ * sessions
+ * @param subtreeUri the subtree on which DMT manipulations can be performed
+ * within the returned session
+ * @param lockMode one of the lock modes specified in
+ * <code>DmtSession</code>
+ * @return a <code>DmtSession</code> object for the requested subtree
+ * @throws DmtException with the following possible error codes:
+ * <ul>
+ * <li><code>URI_TOO_LONG</code> if <code>subtreeUri</code> or
+ * a segment of it is too long, or if it has too many segments
+ * <li><code>INVALID_URI</code> if <code>subtreeUri</code> is
+ * syntactically invalid
+ * <li><code>NODE_NOT_FOUND</code> if <code>subtreeUri</code>
+ * specifies a non-existing node
+ * <li><code>PERMISSION_DENIED</code> if <code>principal</code> is
+ * not <code>null</code> and the ACL of the node does not allow the
+ * <code>Get</code> operation for the principal on the given root
+ * node
+ * <li><code>FEATURE_NOT_SUPPORTED</code> if atomic sessions are
+ * not supported by the implementation and <code>lockMode</code>
+ * requests an atomic session
+ * <li><code>SESSION_CREATION_TIMEOUT</code> if the operation
+ * timed out because of another ongoing session
+ * <li><code>COMMAND_FAILED</code> if <code>subtreeUri</code>
+ * specifies a relative URI, if <code>lockMode</code> is unknown,
+ * or some unspecified error is encountered while attempting to
+ * complete the command
+ * </ul>
+ * @throws SecurityException in case of remote sessions, if the caller does
+ * not have the required <code>DmtPrincipalPermission</code> with a
+ * target matching the <code>principal</code> parameter, or in case
+ * of local sessions, if the caller does not have
+ * <code>DmtPermission</code> for the given root node with the Get
+ * action present
+ */
+ DmtSession getSession(String principal, String subtreeUri, int lockMode)
+ throws DmtException;
+
+ /**
+ * Registers an event listener on behalf of a local application. The given
+ * listener will receive notification on all changes affecting the specified
+ * subtree. The subtree is specified by its root node URI. An event is
+ * delivered to the registered listener if at least one affected node is
+ * within this subtree. The events can also be filtered by specifying a
+ * bitmask of relevant event types (e.g.
+ * <code>DmtEvent.ADDED | DmtEvent.REPLACED | DmtEvent.SESSION_CLOSED</code>).
+ * Only event types included in the bitmask will be delivered to the
+ * listener.
+ * <p>
+ * The listener will only receive the change notifications of nodes for
+ * which the registering application has the appropriate GET
+ * {@link info.dmtree.security.DmtPermission}.
+ * <p>
+ * If the specified <code>listener</code> was already registered, calling
+ * this method will update the registration.
+ *
+ * @param type a bitmask of event types the caller is interested in
+ * @param uri the URI of the root node of a subtree, must not be
+ * <code>null</code>
+ * @param listener the listener to be registered, must not be
+ * <code>null</code>
+ * @throws SecurityException if the caller doesn't have the necessary GET
+ * <code>DmtPermission</code> for the given URI
+ * @throws NullPointerException if the <code>uri</code> or
+ * <code>listener</code> parameter is <code>null</code>
+ * @throws IllegalArgumentException if the <code>type</code> parameter
+ * contains invalid bits (not corresponding to any event type
+ * defined in <code>DmtEvent</code>), or if the <code>uri</code>
+ * parameter is invalid (is not an absolute URI or is syntactically
+ * incorrect)
+ */
+ void addEventListener(int type, String uri, DmtEventListener listener);
+
+ /**
+ * Registers an event listener on behalf of a remote principal. The given
+ * listener will receive notification on all changes affecting the specified
+ * subtree. The subtree is specified by its root node URI. An event is
+ * delivered to the registered listener if at least one affected node is
+ * within this subtree. The events can also be filtered by specifying a
+ * bitmask of relevant event types (e.g.
+ * <code>DmtEvent.ADDED | DmtEvent.REPLACED | DmtEvent.SESSION_CLOSED</code>).
+ * Only event types included in the bitmask will be delivered to the
+ * listener.
+ * <p>
+ * The listener will only receive the change notifications of nodes for
+ * which the node ACL grants GET access to the specified principal.
+ * <p>
+ * If the specified <code>listener</code> was already registered, calling
+ * this method will update the registration.
+ *
+ * @param principal the management server identity the caller is acting on
+ * behalf of, must not be <code>null</code>
+ * @param type a bitmask of event types the caller is interested in
+ * @param uri the URI of the root node of a subtree, must not be
+ * <code>null</code>
+ * @param listener the listener to be registered, must not be
+ * <code>null</code>
+ * @throws SecurityException if the caller doesn't have the necessary
+ * <code>DmtPrincipalPermission</code> to use the specified
+ * principal
+ * @throws NullPointerException if the <code>principal</code>,
+ * <code>uri</code> or <code>listener</code> parameter is
+ * <code>null</code>
+ * @throws IllegalArgumentException if the <code>type</code> parameter
+ * contains invalid bits (not corresponding to any event type
+ * defined in <code>DmtEvent</code>), or if the <code>uri</code>
+ * parameter is invalid (is not an absolute URI or is syntactically
+ * incorrect)
+ */
+ void addEventListener(String principal, int type, String uri,
+ DmtEventListener listener);
+
+ /**
+ * Remove a previously registered listener. After this call, the listener
+ * will not receive change notifications.
+ *
+ * @param listener the listener to be unregistered, must not be
+ * <code>null</code>
+ * @throws NullPointerException if the <code>listener</code> parameter is
+ * <code>null</code>
+ */
+ void removeEventListener(DmtEventListener listener);
+}
Added: felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java?rev=681945&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/info/dmtree/DmtData.java Sat Aug 2 02:56:01 2008
@@ -0,0 +1,907 @@
+/*
+ * $Header: /cvshome/build/info.dmtree/src/info/dmtree/DmtData.java,v 1.8 2006/07/10 21:37:07 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package info.dmtree;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+
+// Possible enhancements to this class:
+// * new constructors and get/set methods for b64, to access the encoded value
+// * new constructors and get/set methods for date/time, for more convenient
+// Java access
+/**
+ * An immutable data structure representing the contents of a leaf or interior
+ * node. This structure represents only the value and the format property of the
+ * node, all other properties (like MIME type) can be set and read using the
+ * <code>DmtSession</code> interface.
+ * <p>
+ * Different constructors are available to create nodes with different formats.
+ * Nodes of <code>null</code> format can be created using the static
+ * {@link #NULL_VALUE} constant instance of this class.
+ * <p>
+ * {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING} enable the support
+ * of future data formats. When using these formats, the actual format name is
+ * specified as a <code>String</code>. The application is responsible for the
+ * proper encoding of the data according to the specified format.
+ */
+public final class DmtData {
+
+ /**
+ * The node holds an OMA DM <code>int</code> value.
+ */
+ public static final int FORMAT_INTEGER = 0x0001;
+
+ /**
+ * The node holds an OMA DM <code>float</code> value.
+ */
+ public static final int FORMAT_FLOAT = 0x0002;
+
+ /**
+ * The node holds an OMA DM <code>chr</code> value.
+ */
+ public static final int FORMAT_STRING = 0x0004;
+
+ /**
+ * The node holds an OMA DM <code>bool</code> value.
+ */
+ public static final int FORMAT_BOOLEAN = 0x0008;
+
+ /**
+ * The node holds an OMA DM <code>date</code> value.
+ */
+ public static final int FORMAT_DATE = 0x0010;
+
+ /**
+ * The node holds an OMA DM <code>time</code> value.
+ */
+ public static final int FORMAT_TIME = 0x0020;
+
+ /**
+ * The node holds an OMA DM <code>bin</code> value. The value of the node
+ * corresponds to the Java <code>byte[]</code> type.
+ */
+ public static final int FORMAT_BINARY = 0x0040;
+
+ /**
+ * The node holds an OMA DM <code>b64</code> value. Like
+ * {@link #FORMAT_BINARY}, this format is also represented by the Java
+ * <code>byte[]</code> type, the difference is only in the corresponding
+ * OMA DM format.
+ */
+ public static final int FORMAT_BASE64 = 0x0080;
+
+ /**
+ * The node holds an OMA DM <code>xml</code> value.
+ */
+ public static final int FORMAT_XML = 0x0100;
+
+ /**
+ * The node holds an OMA DM <code>null</code> value. This corresponds to
+ * the Java <code>null</code> type.
+ */
+ public static final int FORMAT_NULL = 0x0200;
+
+ /**
+ * Format specifier of an internal node. An interior node can hold a Java
+ * object as value (see {@link DmtData#DmtData(Object)} and
+ * {@link DmtData#getNode()}). This value can be used by Java programs that
+ * know a specific URI understands the associated Java type. This type is
+ * further used as a return value of the {@link MetaNode#getFormat} method
+ * for interior nodes.
+ */
+ public static final int FORMAT_NODE = 0x0400;
+
+ /**
+ * The node holds raw protocol data encoded as <code>String</code>. The
+ * {@link #getFormatName()} method can be used to get the actual format
+ * name.
+ */
+ public static final int FORMAT_RAW_STRING = 0x0800;
+
+ /**
+ * The node holds raw protocol data encoded in binary format. The
+ * {@link #getFormatName()} method can be used to get the actual format
+ * name.
+ */
+ public static final int FORMAT_RAW_BINARY = 0x1000;
+
+
+ private static final Hashtable FORMAT_NAMES = new Hashtable();
+
+ static {
+ FORMAT_NAMES.put(new Integer(FORMAT_BASE64), "b64");
+ FORMAT_NAMES.put(new Integer(FORMAT_BINARY), "bin");
+ FORMAT_NAMES.put(new Integer(FORMAT_BOOLEAN), "bool");
+ FORMAT_NAMES.put(new Integer(FORMAT_DATE), "date");
+ FORMAT_NAMES.put(new Integer(FORMAT_FLOAT), "float");
+ FORMAT_NAMES.put(new Integer(FORMAT_INTEGER), "int");
+ FORMAT_NAMES.put(new Integer(FORMAT_NODE), "node");
+ FORMAT_NAMES.put(new Integer(FORMAT_NULL), "null");
+ FORMAT_NAMES.put(new Integer(FORMAT_STRING), "chr");
+ FORMAT_NAMES.put(new Integer(FORMAT_TIME), "time");
+ FORMAT_NAMES.put(new Integer(FORMAT_XML), "xml");
+ }
+
+ /**
+ * Constant instance representing a leaf node of <code>null</code> format.
+ */
+ public static final DmtData NULL_VALUE = new DmtData();
+ // FORMAT_NAMES must be initialized by the time the constr. is called
+
+ private final String str;
+
+ private final int integer;
+
+ private final float flt;
+
+ private final boolean bool;
+
+ private final byte[] bytes;
+
+ private final int format;
+
+ private final String formatName;
+
+ private final Object complex;
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>null</code> format.
+ * This constructor is private and used only to create the public
+ * {@link #NULL_VALUE} constant.
+ */
+ private DmtData() {
+ format = FORMAT_NULL;
+ formatName = getFormatName(format);
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>chr</code> format
+ * with the given string value. The <code>null</code> string argument is
+ * valid.
+ *
+ * @param str the string value to set
+ */
+ public DmtData(String str) {
+ format = FORMAT_STRING;
+ formatName = getFormatName(format);
+ this.str = str;
+
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>node</code> format
+ * with the given object value. The value represents complex data associated
+ * with an interior node.
+ * <p>
+ * Certain interior nodes can support access to their subtrees through such
+ * complex values, making it simpler to retrieve or update all leaf nodes in
+ * a subtree.
+ * <p>
+ * The given value must be a non-<code>null</code> immutable object.
+ *
+ * @param complex the complex data object to set
+ */
+ public DmtData(Object complex) {
+ if(complex == null)
+ throw new NullPointerException("Complex data argument is null.");
+
+ format = FORMAT_NODE;
+ formatName = getFormatName(format);
+ this.complex = complex;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of the specified format and set
+ * its value based on the given string. Only the following string-based
+ * formats can be created using this constructor:
+ * <ul>
+ * <li>{@link #FORMAT_STRING} - value can be any string
+ * <li>{@link #FORMAT_XML} - value must contain an XML fragment (the
+ * validity is not checked by this constructor)
+ * <li>{@link #FORMAT_DATE} - value must be parseable to an ISO 8601
+ * calendar date in complete representation, basic format (pattern
+ * <tt>CCYYMMDD</tt>)
+ * <li>{@link #FORMAT_TIME} - value must be parseable to an ISO 8601 time
+ * of day in either local time, complete representation, basic format
+ * (pattern <tt>hhmmss</tt>) or Coordinated Universal Time, basic format
+ * (pattern <tt>hhmmssZ</tt>)
+ * </ul>
+ * The <code>null</code> string argument is only valid if the format is
+ * string or XML.
+ *
+ * @param value the string, XML, date or time value to set
+ * @param format the format of the <code>DmtData</code> instance to be
+ * created, must be one of the formats specified above
+ * @throws IllegalArgumentException if <code>format</code> is not one of
+ * the allowed formats, or <code>value</code> is not a valid
+ * string for the given format
+ * @throws NullPointerException if a date or time is constructed and
+ * <code>value</code> is <code>null</code>
+ */
+ public DmtData(String value, int format) {
+ switch (format) {
+ case FORMAT_DATE:
+ checkDateFormat(value);
+ break;
+ case FORMAT_TIME:
+ checkTimeFormat(value);
+ break;
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ break; // nothing to do, all string values are accepted
+ default:
+ throw new IllegalArgumentException(
+ "Invalid format in string constructor: " + format);
+ }
+ this.format = format;
+ this.formatName = getFormatName(format);
+ this.str = value;
+
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>int</code> format and
+ * set its value.
+ *
+ * @param integer the integer value to set
+ */
+ public DmtData(int integer) {
+ format = FORMAT_INTEGER;
+ formatName = getFormatName(format);
+ this.integer = integer;
+
+ this.str = null;
+ this.flt = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>float</code> format
+ * and set its value.
+ *
+ * @param flt the float value to set
+ */
+ public DmtData(float flt) {
+ format = FORMAT_FLOAT;
+ formatName = getFormatName(format);
+ this.flt = flt;
+
+ this.str = null;
+ this.integer = 0;
+ this.bool = false;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bool</code> format
+ * and set its value.
+ *
+ * @param bool the boolean value to set
+ */
+ public DmtData(boolean bool) {
+ format = FORMAT_BOOLEAN;
+ formatName = getFormatName(format);
+ this.bool = bool;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bytes = null;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bin</code> format and
+ * set its value.
+ *
+ * @param bytes the byte array to set, must not be <code>null</code>
+ * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+ */
+ public DmtData(byte[] bytes) {
+ if (bytes == null)
+ throw new NullPointerException("Binary data argument is null.");
+
+ format = FORMAT_BINARY;
+ formatName = getFormatName(format);
+ this.bytes = bytes;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance of <code>bin</code> or
+ * <code>b64</code> format and set its value. The chosen format is
+ * specified by the <code>base64</code> parameter.
+ *
+ * @param bytes the byte array to set, must not be <code>null</code>
+ * @param base64 if <code>true</code>, the new instance will have
+ * <code>b64</code> format, if <code>false</code>, it will have
+ * <code>bin</code> format
+ * @throws NullPointerException if <code>bytes</code> is <code>null</code>
+ */
+ public DmtData(byte[] bytes, boolean base64) {
+ if (bytes == null)
+ throw new NullPointerException("Binary data argument is null.");
+
+ format = base64 ? FORMAT_BASE64 : FORMAT_BINARY;
+ formatName = getFormatName(format);
+ this.bytes = bytes;
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_STRING}
+ * format. The data is provided encoded as a <code>String</code>. The
+ * actual data format is specified in <code>formatName</code>. The
+ * encoding used in <code>data</code> must conform to this format.
+ *
+ * @param formatName the name of the format, must not be <code>null</code>
+ * @param data the data encoded according to the specified format, must not
+ * be <code>null</code>
+ * @throws NullPointerException if <code>formatName</code> or
+ * <code>data</code> is <code>null</code>
+ */
+ public DmtData(String formatName, String data) {
+ if(formatName == null)
+ throw new NullPointerException("Format name argument is null.");
+ if(data == null)
+ throw new NullPointerException("Data argument is null.");
+
+ format = FORMAT_RAW_STRING;
+ this.formatName = formatName;
+ this.str = data;
+
+ this.bytes = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Create a <code>DmtData</code> instance in {@link #FORMAT_RAW_BINARY}
+ * format. The data is provided encoded as binary. The actual data format is
+ * specified in <code>formatName</code>. The encoding used in
+ * <code>data</code> must conform to this format.
+ *
+ * @param formatName the name of the format, must not be <code>null</code>
+ * @param data the data encoded according to the specified format, must not
+ * be <code>null</code>
+ * @throws NullPointerException if <code>formatName</code> or
+ * <code>data</code> is <code>null</code>
+ */
+ public DmtData(String formatName, byte[] data) {
+ if(formatName == null)
+ throw new NullPointerException("Format name argument is null.");
+ if(data == null)
+ throw new NullPointerException("Data argument is null.");
+
+ format = FORMAT_RAW_BINARY;
+ this.formatName = formatName;
+ this.bytes = (byte[]) data.clone();
+
+ this.str = null;
+ this.integer = 0;
+ this.flt = 0;
+ this.bool = false;
+ this.complex = null;
+ }
+
+ /**
+ * Gets the value of a node with string (<code>chr</code>) format.
+ *
+ * @return the string value
+ * @throws DmtIllegalStateException if the format of the node is not string
+ */
+ public String getString() {
+ if (format == FORMAT_STRING)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not string.");
+ }
+
+ /**
+ * Gets the value of a node with date format. The returned date string is
+ * formatted according to the ISO 8601 definition of a calendar date in
+ * complete representation, basic format (pattern <tt>CCYYMMDD</tt>).
+ *
+ * @return the date value
+ * @throws DmtIllegalStateException if the format of the node is not date
+ */
+ public String getDate() {
+ if (format == FORMAT_DATE)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not date.");
+ }
+
+ /**
+ * Gets the value of a node with time format. The returned time string is
+ * formatted according to the ISO 8601 definition of the time of day. The
+ * exact format depends on the value the object was initialized with: either
+ * local time, complete representation, basic format (pattern
+ * <tt>hhmmss</tt>) or Coordinated Universal Time, basic format (pattern
+ * <tt>hhmmssZ</tt>).
+ *
+ * @return the time value
+ * @throws DmtIllegalStateException if the format of the node is not time
+ */
+ public String getTime() {
+ if (format == FORMAT_TIME)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not time.");
+ }
+
+ /**
+ * Gets the value of a node with <code>xml</code> format.
+ *
+ * @return the XML value
+ * @throws DmtIllegalStateException if the format of the node is not
+ * <code>xml</code>
+ */
+ public String getXml() {
+ if (format == FORMAT_XML)
+ return str;
+
+ throw new DmtIllegalStateException("DmtData value is not XML.");
+ }
+
+ /**
+ * Gets the value of a node with integer (<code>int</code>) format.
+ *
+ * @return the integer value
+ * @throws DmtIllegalStateException if the format of the node is not integer
+ */
+ public int getInt() {
+ if (format == FORMAT_INTEGER)
+ return integer;
+
+ throw new DmtIllegalStateException("DmtData value is not integer.");
+ }
+
+ /**
+ * Gets the value of a node with <code>float</code> format.
+ *
+ * @return the float value
+ * @throws DmtIllegalStateException if the format of the node is not
+ * <code>float</code>
+ */
+ public float getFloat() {
+ if (format == FORMAT_FLOAT)
+ return flt;
+
+ throw new DmtIllegalStateException("DmtData value is not float.");
+ }
+
+ /**
+ * Gets the value of a node with boolean (<code>bool</code>) format.
+ *
+ * @return the boolean value
+ * @throws DmtIllegalStateException if the format of the node is not boolean
+ */
+ public boolean getBoolean() {
+ if (format == FORMAT_BOOLEAN)
+ return bool;
+
+ throw new DmtIllegalStateException("DmtData value is not boolean.");
+ }
+
+ /**
+ * Gets the value of a node with binary (<code>bin</code>) format.
+ *
+ * @return the binary value
+ * @throws DmtIllegalStateException if the format of the node is not binary
+ */
+ public byte[] getBinary() {
+ if (format == FORMAT_BINARY) {
+ byte[] bytesCopy = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++)
+ bytesCopy[i] = bytes[i];
+
+ return bytesCopy;
+ }
+
+ throw new DmtIllegalStateException("DmtData value is not a byte array.");
+ }
+
+ /**
+ * Gets the value of a node in raw binary ({@link #FORMAT_RAW_BINARY})
+ * format.
+ *
+ * @return the data value in raw binary format
+ * @throws DmtIllegalStateException if the format of the node is not raw binary
+ */
+ public byte[] getRawBinary() {
+ if (format == FORMAT_RAW_BINARY)
+ return (byte[]) bytes.clone();
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in raw binary format.");
+ }
+
+ /**
+ * Gets the value of a node in raw <code>String</code>
+ * ({@link #FORMAT_RAW_STRING}) format.
+ *
+ * @return the data value in raw <code>String</code> format
+ * @throws DmtIllegalStateException if the format of the node is not raw
+ * <code>String</code>
+ */
+ public String getRawString() {
+ if (format == FORMAT_RAW_STRING)
+ return str;
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in raw string format.");
+ }
+
+ /**
+ * Gets the value of a node with base 64 (<code>b64</code>) format.
+ *
+ * @return the binary value
+ * @throws DmtIllegalStateException if the format of the node is not base 64.
+ */
+ public byte[] getBase64() {
+ if (format == FORMAT_BASE64) {
+ byte[] bytesCopy = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++)
+ bytesCopy[i] = bytes[i];
+
+ return bytesCopy;
+ }
+
+ throw new DmtIllegalStateException(
+ "DmtData value is not in base 64 format.");
+ }
+
+ /**
+ * Gets the complex data associated with an interior node (<code>node</code>
+ * format).
+ * <p>
+ * Certain interior nodes can support access to their subtrees through
+ * complex values, making it simpler to retrieve or update all leaf nodes in
+ * the subtree.
+ *
+ * @return the data object associated with an interior node
+ * @throws DmtIllegalStateException if the format of the data is not
+ * <code>node</code>
+ */
+ public Object getNode() {
+ if(format == FORMAT_NODE)
+ return complex;
+
+ throw new DmtIllegalStateException(
+ "DmtData does not contain interior node data.");
+ }
+
+ /**
+ * Get the node's format, expressed in terms of type constants defined in
+ * this class. Note that the 'format' term is a legacy from OMA DM, it is
+ * more customary to think of this as 'type'.
+ *
+ * @return the format of the node
+ */
+ public int getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns the format of this <code>DmtData</code> as <code>String</code>.
+ * For the predefined data formats this is the OMA DM defined name of the
+ * format. For {@link #FORMAT_RAW_STRING} and {@link #FORMAT_RAW_BINARY}
+ * this is the format specified when the object was created.
+ *
+ * @return the format name as <code>String</code>
+ */
+ public String getFormatName() {
+ return formatName;
+ }
+
+ /**
+ * Get the size of the data. The returned value depends on the format of
+ * data in the node:
+ * <ul>
+ * <li>{@link #FORMAT_STRING}, {@link #FORMAT_XML}, {@link #FORMAT_BINARY},
+ * {@link #FORMAT_BASE64}, {@link #FORMAT_RAW_STRING}, and
+ * {@link #FORMAT_RAW_BINARY}: the length of the stored data, or 0 if
+ * the data is <code>null</code>
+ * <li>{@link #FORMAT_INTEGER} and {@link #FORMAT_FLOAT}: 4
+ * <li>{@link #FORMAT_DATE} and {@link #FORMAT_TIME}: the length of the
+ * date or time in its string representation
+ * <li>{@link #FORMAT_BOOLEAN}: 1
+ * <li>{@link #FORMAT_NODE}: -1 (unknown)
+ * <li>{@link #FORMAT_NULL}: 0
+ * </ul>
+ *
+ * @return the size of the data stored by this object
+ */
+ public int getSize() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? 0 : str.length();
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return bytes.length;
+ case FORMAT_INTEGER:
+ case FORMAT_FLOAT:
+ return 4;
+ case FORMAT_BOOLEAN:
+ return 1;
+ case FORMAT_NODE:
+ return -1;
+ case FORMAT_NULL:
+ return 0;
+ }
+
+ return 0; // never reached
+ }
+
+ /**
+ * Gets the string representation of the <code>DmtData</code>. This
+ * method works for all formats.
+ * <p>
+ * For string format data - including {@link #FORMAT_RAW_STRING} - the
+ * string value itself is returned, while for XML, date, time, integer,
+ * float, boolean and node formats the string form of the value is returned.
+ * Binary - including {@link #FORMAT_RAW_BINARY} - and base64 data is
+ * represented by two-digit hexadecimal numbers for each byte separated by
+ * spaces. The {@link #NULL_VALUE} data has the string form of
+ * "<code>null</code>". Data of string or XML format containing the Java
+ * <code>null</code> value is represented by an empty string.
+ *
+ * @return the string representation of this <code>DmtData</code> instance
+ */
+ public String toString() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? "" : str;
+ case FORMAT_INTEGER:
+ return String.valueOf(integer);
+ case FORMAT_FLOAT:
+ return String.valueOf(flt);
+ case FORMAT_BOOLEAN:
+ return String.valueOf(bool);
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return getHexDump(bytes);
+ case FORMAT_NODE:
+ return complex.toString();
+ case FORMAT_NULL:
+ return "null";
+ }
+
+ return null; // never reached
+ }
+
+ /**
+ * Compares the specified object with this <code>DmtData</code> instance.
+ * Two <code>DmtData</code> objects are considered equal if their format
+ * is the same, and their data (selected by the format) is equal.
+ * <p>
+ * In case of {@link #FORMAT_RAW_BINARY} and {@link #FORMAT_RAW_STRING}
+ * the textual name of the data format - as returned by
+ * {@link #getFormatName()} - must be equal as well.
+ *
+ * @param obj the object to compare with this <code>DmtData</code>
+ * @return true if the argument represents the same <code>DmtData</code>
+ * as this object
+ */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DmtData))
+ return false;
+
+ DmtData other = (DmtData) obj;
+
+ if (format != other.format)
+ return false;
+
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ return str == null ? other.str == null : str.equals(other.str);
+ case FORMAT_INTEGER:
+ return integer == other.integer;
+ case FORMAT_FLOAT:
+ return flt == other.flt;
+ case FORMAT_BOOLEAN:
+ return bool == other.bool;
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ return Arrays.equals(bytes, other.bytes);
+ case FORMAT_NODE:
+ return complex.equals(other.complex);
+ case FORMAT_NULL:
+ return true;
+ case FORMAT_RAW_BINARY:
+ return formatName.equals(other.formatName)
+ && Arrays.equals(bytes, other.bytes);
+ case FORMAT_RAW_STRING:
+ // in this case str cannot be null
+ return formatName.equals(other.formatName) && str.equals(other.str);
+ }
+
+ return false; // never reached
+ }
+
+ /**
+ * Returns the hash code value for this <code>DmtData</code> instance. The
+ * hash code is calculated based on the data (selected by the format) of
+ * this object.
+ *
+ * @return the hash code value for this object
+ */
+ public int hashCode() {
+ switch (format) {
+ case FORMAT_STRING:
+ case FORMAT_XML:
+ case FORMAT_DATE:
+ case FORMAT_TIME:
+ case FORMAT_RAW_STRING:
+ return str == null ? 0 : str.hashCode();
+ case FORMAT_INTEGER:
+ return new Integer(integer).hashCode();
+ case FORMAT_FLOAT:
+ return new Float(flt).hashCode();
+ case FORMAT_BOOLEAN:
+ return new Boolean(bool).hashCode();
+ case FORMAT_BINARY:
+ case FORMAT_BASE64:
+ case FORMAT_RAW_BINARY:
+ return new String(bytes).hashCode();
+ case FORMAT_NODE:
+ return complex.hashCode();
+ case FORMAT_NULL:
+ return 0;
+ }
+
+ return 0; // never reached
+ }
+
+ private static void checkDateFormat(String value) {
+ if(value.length() != 8)
+ throw new IllegalArgumentException("Date string '" + value +
+ "' does not follow the format 'CCYYMMDD'.");
+
+ int year = checkNumber(value, "Date", 0, 4, 0, 9999);
+ int month = checkNumber(value, "Date", 4, 2, 1, 12);
+ int day = checkNumber(value, "Date", 6, 2, 1, 31);
+
+ // Date checking is not prepared for all special rules (for example
+ // historical leap years), production code could contain a full check.
+
+ // Day 31 is invalid for April, June, September and November
+ if((month == 4 || month == 6 || month == 9 || month == 11) && day == 31)
+ throw new IllegalArgumentException("Date string '" + value +
+ "' contains an invalid date.");
+
+ // February 29 is invalid except for leap years, Feb. 30-31 are invalid
+ if(month == 2 && day > 28 &&
+ !(day == 29 && year%4 == 0 && (year%100 != 0 || year%400 == 0)))
+ throw new IllegalArgumentException("Date string '" + value +
+ "' contains an invalid date.");
+ }
+
+ private static void checkTimeFormat(String value) {
+ if(value.length() > 0 && value.charAt(value.length()-1) == 'Z')
+ value = value.substring(0, value.length()-1);
+
+ if(value.length() != 6)
+ throw new IllegalArgumentException("Time string '" + value +
+ "' does not follow the format 'hhmmss' or 'hhmmssZ'.");
+
+ // Time checking is not prepared for all special rules (for example
+ // leap seconds), production code could contain a full check.
+
+ // if hour is 24, only 240000 should be allowed
+ checkNumber(value, "Time", 0, 2, 0, 24);
+ checkNumber(value, "Time", 2, 2, 0, 59);
+ checkNumber(value, "Time", 4, 2, 0, 59);
+
+ if(value.startsWith("24") && !value.startsWith("240000"))
+ throw new IllegalArgumentException("Time string is out of range.");
+ }
+
+ private static int checkNumber(String value, String name, int from,
+ int length, int min, int max) {
+ String part = value.substring(from, from+length);
+ int number;
+ try {
+ number = Integer.parseInt(part);
+ } catch(NumberFormatException e) {
+ throw new IllegalArgumentException(name + " string '" + value +
+ "' contains a non-numeric part.");
+ }
+ if(number < min || number > max)
+ throw new IllegalArgumentException("A segment of the " + name +
+ " string '" + value + "' is out of range.");
+
+ return number;
+ }
+
+ // character array of hexadecimal digits, used for printing binary data
+ private static char[] hex = "0123456789ABCDEF".toCharArray();
+
+ // generates a hexadecimal dump of the given binary data
+ private static String getHexDump(byte[] bytes) {
+ if (bytes.length == 0)
+ return "";
+
+ StringBuffer buf = new StringBuffer();
+ appendHexByte(buf, bytes[0]);
+ for (int i = 1; i < bytes.length; i++)
+ appendHexByte(buf.append(' '), bytes[i]);
+
+ return buf.toString();
+ }
+
+ private static void appendHexByte(StringBuffer buf, byte b) {
+ buf.append(hex[(b & 0xF0) >> 4]).append(hex[b & 0x0F]);
+ }
+
+ private static String getFormatName(int format) {
+ return (String) FORMAT_NAMES.get(new Integer(format));
+ }
+}