Graduation of the bundle from the equinox.incubator
diff --git a/bundles/org.eclipse.equinox.ds/.classpath b/bundles/org.eclipse.equinox.ds/.classpath
index acad1c2..755d49b 100644
--- a/bundles/org.eclipse.equinox.ds/.classpath
+++ b/bundles/org.eclipse.equinox.ds/.classpath
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/OSGi%Minimum-1.0"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.core.prefs
index 4a4dc43..d304116 100644
--- a/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.core.prefs
+++ b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.core.prefs
@@ -1,23 +1,49 @@
-#Mon Feb 26 10:38:16 EST 2007
+#Thu Aug 16 11:00:59 EDT 2007
eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=1000
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
@@ -25,7 +51,6 @@
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
@@ -37,16 +62,17 @@
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
org.eclipse.jdt.core.compiler.problem.unusedImport=error
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
@@ -292,9 +318,14 @@
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=800
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.ui.prefs
index 9bf712c..023a27a 100644
--- a/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.ui.prefs
+++ b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.jdt.ui.prefs
@@ -1,8 +1,57 @@
-#Mon Feb 26 10:45:44 EST 2007
+#Tue Aug 21 11:19:11 CDT 2007
eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_core
formatter_settings_version=11
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=;
org.eclipse.jdt.ui.ondemandthreshold=3
org.eclipse.jdt.ui.staticondemandthreshold=99
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..c5cfeff
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Mar 29 10:39:03 EEST 2007
+eclipse.preferences.version=1
+pluginProject.extensions=false
diff --git a/bundles/org.eclipse.equinox.ds/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.ds/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9979b7a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/META-INF/MANIFEST.MF
@@ -0,0 +1,43 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Declarative Services
+Bundle-SymbolicName: org.eclipse.equinox.ds
+Bundle-Version: 0.1.0.qualifier
+Bundle-Vendor: Eclipse
+Bundle-Activator: org.eclipse.equinox.internal.ds.Activator
+Bundle-Description: This bundle provides support for OSGi
+ Declarative Services. This module implements a declarative service model,
+ which allows a bundle to delay instantiating the service object until
+ they are needed, and thus minimizing the resource consumption at
+ any point of time. That module is also one of the core components for
+ the OSGi R4.
+Import-Package:
+ org.eclipse.equinox.internal.util.event;version="1.0",
+ org.eclipse.equinox.internal.util.hash;version="1.0",
+ org.eclipse.equinox.internal.util.pool;version="1.0",
+ org.eclipse.equinox.internal.util.ref;version="1.0",
+ org.eclipse.equinox.internal.util.threadpool;version="1.0",
+ org.eclipse.equinox.internal.util.timer;version="1.0",
+ org.eclipse.osgi.util,
+ org.osgi.framework;version="1.3",
+ org.osgi.service.cm;version="1.2",
+ org.osgi.service.component;version="1.0",
+ org.osgi.service.metatype;version="1.1",
+ org.osgi.util.tracker;version="1.3"
+Export-Package:
+ org.eclipse.equinox.internal.ds;x-internal:=true,
+ org.eclipse.equinox.internal.ds.impl;x-internal:=true,
+ org.eclipse.equinox.internal.ds.model;x-internal:=true,
+ org.eclipse.equinox.internal.ds.storage.file;x-internal:=true,
+ org.eclipse.equinox.internal.util.io;x-internal:=true,
+ org.eclipse.equinox.internal.util.xml;x-internal:=true,
+ org.eclipse.equinox.internal.util.xml.impl;x-internal:=true,
+ org.eclipse.equinox.internal.util.string;x-internal:=true
+Bundle-RequiredExecutionEnvironment: OSGi/Minimum-1.0,
+ CDC-1.0/Foundation-1.0,
+ J2SE-1.2,
+ J2SE-1.3,
+ J2SE-1.4,
+ J2SE-1.5,
+ JavaSE-1.6
+Lazy-ManifestFilter: (Service-Component=*)
diff --git a/bundles/org.eclipse.equinox.ds/OSGI-INF/permissions.perm b/bundles/org.eclipse.equinox.ds/OSGI-INF/permissions.perm
new file mode 100644
index 0000000..5a96587
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/OSGI-INF/permissions.perm
@@ -0,0 +1 @@
+(java.security.AllPermission "*" "*")
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.ds/about.html b/bundles/org.eclipse.equinox.ds/about.html
new file mode 100644
index 0000000..40e147d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>January 15, 2008</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/bundles/org.eclipse.equinox.ds/build.properties b/bundles/org.eclipse.equinox.ds/build.properties
new file mode 100644
index 0000000..9cbab3c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html
+src.includes = about.html
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Activator.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Activator.java
new file mode 100644
index 0000000..46b2f0b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Activator.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.util.Dictionary;
+import org.eclipse.equinox.internal.util.ref.Log;
+import org.osgi.framework.*;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.service.component.ComponentConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * This is the main starting class for the Service Component Runtime.
+ *
+ * It also acts as a "Bundle Manager" - a listener for bundle events. Whenever a
+ * bundle is stopped or started it will invoke the resolver to respectively
+ * enable or disable neccessary service components.
+ *
+ * Notice, the SynchronousBundleListener bundle listeners are called prior
+ * bundle event is completed. This will make the stuff a little bit faster ;)
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.1
+ */
+
+public class Activator implements BundleActivator, SynchronousBundleListener {
+
+ public static BundleContext bc = null;
+
+ private ServiceTracker cmTracker;
+ private ServiceRegistration cmTrackerReg;
+
+ private SCRManager scrManager = null;
+
+ private boolean inited = false;
+
+ public static Log log;
+ public static boolean DEBUG;
+ public static boolean PERF;
+ public static boolean DBSTORE;
+ public static boolean startup;
+
+ static long time[] = null;
+
+ public static void timeLog(int id) {
+ time[1] = time[0];
+ log.debug(0x0100, id, String.valueOf((time[0] = System.currentTimeMillis()) - time[1]), null, false, true);
+ }
+
+ private void initSCR() {
+ synchronized (this) {
+ if (inited)
+ return;
+ inited = true;
+ }
+
+ boolean lazyIniting = false;
+ if (startup && time == null) {
+ long tmp = System.currentTimeMillis();
+ time = new long[] {tmp, 0, tmp};
+ lazyIniting = true;
+ if (startup)
+ timeLog(114); /* 114 = "[BEGIN - lazy SCR init] " */
+ }
+
+ WorkThread.IDLE_TIMEOUT = getInteger("equinox.ds.idle_timeout", 1000);
+ WorkThread.BLOCK_TIMEOUT = getInteger("equinox.ds.block_timeout", 30000);
+
+ // start the config tracker
+ cmTracker = new ServiceTracker(bc, ConfigurationAdmin.class.getName(), null);
+
+ if (startup)
+ timeLog(102);
+ /*102 = "ConfigurationAdmin ServiceTracker instantiation took "*/
+
+ ConfigurationManager.cmTracker = cmTracker;
+ cmTracker.open();
+ if (startup)
+ timeLog(103); /* 103 = "ServiceTracker starting took " */
+
+ scrManager = new SCRManager(bc, log);
+ if (startup)
+ timeLog(104); /* 104 = "SCRManager instantiation took " */
+
+ // add the configuration listener - we to receive CM events to restart
+ // components
+ cmTrackerReg = bc.registerService(ConfigurationListener.class.getName(), scrManager, null);
+ if (startup)
+ timeLog(106); /*106 = "ConfigurationListener service registered for "*/
+ bc.addServiceListener(scrManager);
+ if (startup)
+ timeLog(107); /* 107 = "addServiceListener() method took " */
+
+ scrManager.startIt();
+ if (Activator.startup)
+ Activator.timeLog(113); /* 113 = "startIt() method took " */
+
+ if (startup && lazyIniting) {
+ /* 115 = "[END - lazy SCR init] Activator.initSCR() method executed for "*/
+ log.debug(0x0100, 115, String.valueOf(time[0] - time[2]), null, false);
+ time = null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bc) throws Exception {
+ Activator.bc = bc;
+ startup = getBoolean("equinox.measurements.full") || getBoolean("equinox.measurements.bundles");
+ if (startup) {
+ long tmp = System.currentTimeMillis();
+ time = new long[] {tmp, 0, tmp};
+ }
+ // initialize the logging routines
+ log = new Log(bc, false);
+ DEBUG = getBoolean("equinox.ds.debug");
+ PERF = getBoolean("equinox.ds.perf");
+ DBSTORE = getBoolean("equinox.ds.dbstore");
+ log.setDebug(DEBUG);
+ boolean print = getBoolean("equinox.ds.print");
+ log.setPrintOnConsole(print);
+ if (DEBUG) {
+ log.setMaps(TracerMap.getMap(), TracerMap.getStarts());
+ }
+
+ if (startup)
+ timeLog(100);
+ /*
+ * 100 = "[BEGIN - start method] Creating Log
+ * instance and initializing log system took "
+ */
+
+ boolean hasHeaders = false;
+ Bundle[] allBundles = bc.getBundles();
+ for (int i = 0; i < allBundles.length; i++) {
+ Dictionary allHeaders = allBundles[i].getHeaders();
+ if (allHeaders.get(ComponentConstants.SERVICE_COMPONENT) != null) {
+ hasHeaders = true;
+ break;
+ }
+ }
+
+ if (hasHeaders) {
+ initSCR();
+ } else {
+ // there are no bundles holding components - SCR will not be
+ // initialized yet
+ bc.addBundleListener(this);
+ }
+ if (startup) {
+ log.debug(0x0100, 108, String.valueOf(time[0] - time[2]), null, false);
+ time = null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bc) throws Exception {
+ log.info("Shutting down service component runtime!");
+ if (scrManager != null) {
+ bc.removeServiceListener(scrManager);
+ }
+ // dispose the CM Listener
+ if (cmTrackerReg != null) {
+ cmTrackerReg.unregister();
+ }
+ if (scrManager != null) {
+ bc.removeBundleListener(scrManager);
+ } else {
+ bc.removeBundleListener(this);
+ }
+
+ // untrack the cm!
+ if (cmTracker != null) {
+ ConfigurationManager.cmTracker = null;
+ cmTracker.close();
+ cmTracker = null;
+ }
+
+ if (scrManager != null) {
+ scrManager.stopIt();
+ }
+ log.close();
+ log = null;
+ bc = null;
+ }
+
+ public static Filter createFilter(String filter) throws InvalidSyntaxException {
+ return bc.createFilter(filter);
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.STARTING) {
+ Dictionary allHeaders = event.getBundle().getHeaders();
+ if ((allHeaders.get(ComponentConstants.SERVICE_COMPONENT)) != null) {
+ // The bundle is holding components - activate scr
+ bc.removeBundleListener(this);
+ initSCR();
+ }
+ }
+ }
+
+ public static boolean getBoolean(String property) {
+ String prop = (bc != null) ? bc.getProperty(property) : System.getProperty(property);
+ return ((prop != null) && prop.equalsIgnoreCase("true"));
+ }
+
+ public static int getInteger(String property, int defaultValue) {
+ String prop = (bc != null) ? bc.getProperty(property) : System.getProperty(property);
+ if (prop != null) {
+ try {
+ return Integer.decode(prop).intValue();
+ } catch (NumberFormatException e) {
+ //do nothing
+ }
+ }
+ return defaultValue;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/CircularityException.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/CircularityException.java
new file mode 100644
index 0000000..9073f4b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/CircularityException.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+
+/**
+ * Used to find circular dependencies
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class CircularityException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private ServiceComponentProp causingComponent;
+
+ public CircularityException(ServiceComponentProp scp) {
+ this.causingComponent = scp;
+ }
+
+ public ServiceComponentProp getCausingComponent() {
+ return causingComponent;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ComponentStorage.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ComponentStorage.java
new file mode 100644
index 0000000..6625008
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ComponentStorage.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.equinox.internal.ds.model.DeclarationParser;
+import org.osgi.framework.Bundle;
+import org.osgi.service.component.ComponentConstants;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public abstract class ComponentStorage {
+
+ private final DeclarationParser parser = new DeclarationParser();
+
+ /**
+ * This method will load the component definitions from a bundle. The
+ * returned value should contain vector with 'ServiceComponent' elements.
+ *
+ * @param bundleID
+ * id of the bundle, which is processed and if contains a
+ * component definitions - it's xml definition file got parsed.
+ * @return <code>null</code> if there are no component definitions.
+ */
+ public abstract Vector loadComponentDefinitions(long bundleID);
+
+ public abstract void deleteComponentDefinitions(long bundleID) throws Exception;
+
+ public abstract void stop();
+
+ protected Vector parseXMLDeclaration(Bundle bundle) throws Exception {
+ Vector components = new Vector();
+ Dictionary headers = bundle.getHeaders(null);
+ String header = (String) headers.get(ComponentConstants.SERVICE_COMPONENT);
+
+ if (header != null) {
+ StringTokenizer tok = new StringTokenizer(header, ",");
+ // the parser is not thread safe!!!
+ synchronized (parser) {
+ // process all definition file
+ while (tok.hasMoreElements()) {
+ String definitionFile = tok.nextToken().trim();
+ int ind = definitionFile.lastIndexOf('/');
+ String path = ind != -1 ? definitionFile.substring(0, ind) : "/";
+ InputStream is = null;
+
+ Enumeration urls = bundle.findEntries(path, ind != -1 ? definitionFile.substring(ind + 1) : definitionFile, false);
+ if (urls == null || !urls.hasMoreElements()) {
+ Activator.log.error("Component definition XMLs not found in bundle " + bundle.getSymbolicName() + ". The component header value is " + definitionFile, null);
+ continue;
+ }
+
+ // illegal components are ignored, but framework event is posted for
+ // them; however, it will continue and try to load any legal
+ // definitions
+ URL url;
+ while (urls.hasMoreElements()) {
+ url = (URL) urls.nextElement();
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10017, url.toString(), null, false);
+ ////Activator.log.debug("ComponentStorage.parseXMLDeclaration(): loading " + definitionFile, null);
+ }
+ try {
+ is = url.openStream();
+ if (is == null) {
+ Activator.log.error("ComponentStorage.parseXMLDeclaration(): missing file " + url, null);
+ } else {
+ parser.parse(is, bundle, components);
+ }
+ } catch (IOException ie) {
+ Activator.log.error("[SCR] Error occured while opening component definition file " + url, ie);
+ } catch (Throwable t) {
+ Activator.log.error("Illegal definition file: " + url, t);
+ }
+ }
+ } // end while
+
+ components = parser.components;
+ // make sure the clean-up the parser cache, for the next bundle to
+ // work properly!!!
+ parser.components = null;
+ }
+
+ }
+ return components;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ConfigurationManager.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ConfigurationManager.java
new file mode 100644
index 0000000..1ebe428
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ConfigurationManager.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.io.IOException;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * ConfigurationManager.java
+ *
+ * @author Valentin Valchev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+class ConfigurationManager {
+
+ static ServiceTracker cmTracker;
+
+ static Configuration getConfiguration(String pid) throws IOException {
+ ConfigurationAdmin cmAdmin = (ConfigurationAdmin) cmTracker.getService();
+ if (cmAdmin != null) {
+ return cmAdmin.getConfiguration(pid);
+ }
+ return null;
+ }
+
+ static Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException {
+ ConfigurationAdmin cmAdmin = (ConfigurationAdmin) cmTracker.getService();
+ if (cmAdmin != null) {
+ return cmAdmin.listConfigurations(filter);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/FactoryReg.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/FactoryReg.java
new file mode 100644
index 0000000..fd50fa6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/FactoryReg.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+import org.osgi.framework.*;
+import org.osgi.service.component.ComponentException;
+import org.osgi.service.component.ComponentInstance;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+final class FactoryReg implements ServiceFactory {
+
+ /* the instance created */
+ private final ServiceComponentProp component;
+
+ FactoryReg(ServiceComponentProp component) {
+ this.component = component;
+ }
+
+ // ServiceFactory.getService method.
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10001, component.name, null, false);
+ // //Activator.log.debug("FactoryReg.getService(): created new
+ // service for component '" + component.name, null);
+ }
+ ComponentInstance ci = InstanceProcess.staticRef.buildComponent(bundle, component, null, false);
+ // ci can be null if the component is already disposed while being built
+ if (ci != null) {
+ return ci.getInstance();
+ }
+ } catch (Throwable t) {
+ if (!(t instanceof ComponentException)) {
+ Activator.log.error("RegisterComponentService: Cannot create instance of " + component.name, t);
+ } else {
+ throw (ComponentException) t;
+ }
+ }
+ return null;
+ }
+
+ // ServiceFactory.ungetService method.
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10002, registration.toString(), null, false);
+ }
+
+ component.disposeObj(service);
+ }
+
+ public String toString() {
+ return component.name + " FactoryRegistration";
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/InstanceProcess.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/InstanceProcess.java
new file mode 100644
index 0000000..00ffede
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/InstanceProcess.java
@@ -0,0 +1,735 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.util.*;
+import org.eclipse.equinox.internal.ds.impl.ComponentFactoryImpl;
+import org.eclipse.equinox.internal.ds.impl.ComponentInstanceImpl;
+import org.eclipse.equinox.internal.ds.model.*;
+import org.osgi.framework.*;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.component.*;
+
+/**
+ * This class is responsible for creating, tracking and disposing of service
+ * instances and registrations.
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.2
+ */
+
+public class InstanceProcess {
+
+ public static Resolver resolver;
+ public static InstanceProcess staticRef;
+
+ /** map SCP:ServiceRegistration */
+ protected Hashtable factoryRegistrations;
+
+ /**
+ * Used with stackCount to handle circular dependencies in the
+ * {@link InstanceProcess#buildComponent(Bundle, ServiceComponentProp, Object)}
+ * method.
+ */
+ private Vector delayedBindList;
+
+ //key - the SPC being built; value - the thread that builds the SCP
+ static Hashtable buildingThreads = new Hashtable(7);
+ //key - the building thread; value - Counter - holds the count of entries in buildComponent method
+ static Hashtable stackCounts = new Hashtable(7);
+ //specifies the maximum time that a thread must wait for the building thread to complete the building of the SCP
+ static int waitTime = Activator.getInteger("equinox.scr.waitTimeOnBlock", 10000);
+
+ //a flag used for synchronization of build/dispose operations
+ boolean busyBuilding = false;
+ //the working thread that performs the current build/dispose operation
+ Thread workingThread;
+ //an object used for synchronization when changing the status of busyBuilding flag
+ Object lock = new Object();
+ //used to count the number of times a lock is held when required recursively
+ int lockCounter = 0;
+
+ /**
+ * Handle Instance processing building and disposing.
+ *
+ * @param resolver
+ * the resolver instance
+ */
+ InstanceProcess(Resolver resolver) {
+ InstanceProcess.resolver = resolver;
+ factoryRegistrations = new Hashtable(19);
+ delayedBindList = new Vector(10);
+ staticRef = this;
+ }
+
+ /**
+ * dispose cleanup the SCR is shutting down
+ */
+ void dispose() {
+ factoryRegistrations = null;
+ }
+
+ // gets the synch lock to perform some build/release work
+ void getLock() {
+ synchronized (lock) {
+ Thread currentThread = Thread.currentThread();
+ if (!busyBuilding) {
+ busyBuilding = true;
+ lockCounter++;
+ workingThread = currentThread;
+ } else if (workingThread == currentThread) {
+ //increase the lock counter - the lock is required recursively
+ lockCounter++;
+ } else if (workingThread != currentThread) {
+ long start = System.currentTimeMillis();
+ long timeToWait = waitTime;
+ boolean lockSucceeded = false;
+ do {
+ try {
+ lock.wait(timeToWait);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ if (!busyBuilding) {
+ busyBuilding = true;
+ lockCounter++;
+ workingThread = currentThread;
+ lockSucceeded = true;
+ break;
+ }
+ timeToWait = waitTime + start - System.currentTimeMillis();
+ } while (timeToWait > 0);
+ //check if the timeout has passed or the lock is actually successfully held
+ if (!lockSucceeded) {
+ // The lock is not yet released!
+ // Allow the operation but log a warning
+ Activator.log.warning("Getting a lock required more than " + InstanceProcess.waitTime + " ms. There might be a synchronization problem in this callstack " + "or just the build/dispose process of some components took too long!", new Exception("Debug stacktrace"));
+ }
+ }
+ }
+ }
+
+ // free the synch lock
+ void freeLock() {
+ synchronized (lock) {
+ if (busyBuilding) {
+ if (workingThread == Thread.currentThread()) {
+ //only the thread holding the lock can release it
+ lockCounter--;
+ }
+ // release the lock in case the lock counter has decreased to 0
+ if (lockCounter == 0) {
+ busyBuilding = false;
+ workingThread = null;
+ lock.notify();
+ }
+ }
+ }
+ }
+
+ /**
+ * Builds the Service Component descriptions (registration of needed
+ * services, building of component instances if necessary (includes
+ * activating and binding)
+ *
+ * @param list -
+ * a Vector of all components to build.
+ */
+ public void buildComponents(Vector list, boolean security) {
+ ServiceComponentProp scp = null;
+ ServiceComponent sc;
+ String factoryPid = null;
+
+ // loop through SCP list of enabled
+ if (list != null) {
+ for (int i = 0; i < list.size(); i++) {
+ scp = (ServiceComponentProp) list.elementAt(i);
+ if (scp.disposed)
+ continue;
+ getLock();
+ if (scp.disposed || !InstanceProcess.resolver.satisfiedSCPs.contains(scp)) {
+ //no need to build the component - it is disposed or about to be disposed
+ freeLock();
+ continue;
+ }
+ long start = 0l;
+ try {
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ Activator.log.info("[DS perf] Start building component " + scp);
+ }
+ sc = scp.serviceComponent;
+ if (sc.immediate) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10003, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.buildComponents():
+ // building immediate component " + scp.name, null);
+ }
+ if (sc.serviceInterfaces != null) {
+ // this component registers service
+
+ // this will create either plain service component
+ // registration
+ // or a service factory registration
+ registerService(scp, sc.serviceFactory, null);
+ }
+ if (scp.instances.isEmpty()) {
+ // nobody has required the service after registration
+ // (and no service instance could be created),
+ // so we need to force instance creation
+ try {
+ buildComponent(null, scp, null, security);
+ } catch (Exception e) {
+ if (!(e instanceof ComponentException)) {
+ Activator.log.error("[SCR] Cannot build component " + scp, e);
+ }
+ }
+ }
+ } else {
+
+ // ComponentFactory
+ if (sc.factory != null) {
+ // check if it is NOT a component config created by a
+ // component factory
+ if (scp.isComponentFactory()) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10004, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.buildComponents():
+ // building component factory " + scp.name,
+ // null);
+ }
+
+ // check if MSF
+ try {
+ Configuration config = ConfigurationManager.getConfiguration(sc.name);
+ if (config != null) {
+ factoryPid = config.getFactoryPid();
+ }
+ } catch (Exception e) {
+ Activator.log.error("[SCR] Cannot get configuration for component " + sc.name, e);
+ }
+
+ // if MSF throw exception - can't be
+ // ComponentFactory add MSF
+ if (factoryPid != null) {
+ throw new org.osgi.service.component.ComponentException("ManagedServiceFactory and ComponentFactory are incompatible");
+ }
+ registerComponentFactory(scp);
+ // when registering a ComponentFactory we must not
+ // reguister
+ // the component configuration as service
+ continue;
+ }
+ }
+
+ // check whether there is a service to register
+ if (sc.provides != null) {
+
+ // this will create either plain service component
+ // registration
+ // or a service factory registration
+ registerService(scp, sc.serviceFactory, null);
+ }
+ }
+ } catch (Throwable t) {
+ Activator.log.error("Exception occured while building component " + scp, t);
+ } finally {
+ freeLock();
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ Activator.log.info("[DS perf] The component " + scp + " is built for " + start + " ms");
+ }
+ }
+ } // end for
+ } // end if (list != null)
+ }
+
+ /**
+ *
+ * Dispose of Component Instances, includes unregistering services and
+ * removing instances.
+ *
+ * @param scpList -
+ * list of ComponentDescriptions plus Property objects to be
+ * disposed
+ */
+ void disposeInstances(Vector scpList) {
+ // loop through SC+P list to be disposed
+ if (scpList != null) {
+ for (int i = 0; i < scpList.size(); i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) scpList.elementAt(i);
+ if (scp.disposed)
+ continue;
+ getLock();
+ if (scp.disposed) {
+ freeLock();
+ continue;
+ }
+ long start = 0l;
+ try {
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ Activator.log.info("[DS perf] Start disposing component " + scp);
+ }
+ disposeInstances(scp);
+ } catch (Throwable t) {
+ Activator.log.error("Exception while disposing instances of component " + scp, t);
+ } finally {
+ freeLock();
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ Activator.log.info("[DS perf] The component " + scp + " is disposed for " + start + " ms");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param scp
+ */
+ private void disposeInstances(ServiceComponentProp scp) {
+ ServiceComponent sc = scp.serviceComponent;
+ // if no Services provided - dispose of instance immediately
+ if (sc.provides == null) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10006, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.disposeInstances():
+ // disposing non-provider component " + scp.name, null);
+ }
+ scp.dispose();
+ } else {
+ // if ComponentFactory or if just Services
+ if (scp.isComponentFactory()) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10007, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.disposeInstances():
+ // disposing component factory " + scp.name, null);
+ }
+ ServiceRegistration reg = (ServiceRegistration) factoryRegistrations.remove(scp);
+ try {
+ if (reg != null)
+ reg.unregister();
+ } catch (IllegalStateException e) {
+ // Service is already unregistered do nothing
+ Activator.log.warning("InstanceProcess.disposeInstances(): registration for component factory " + scp.name + " is already diposed!", null);
+ }
+ }
+
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10008, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.disposeInstances():
+ // unregistering component " + scp.name, null);
+ }
+
+ // unregister services if any
+ if (scp.registration != null) {
+ try {
+ ServiceRegistration reg = scp.registration;
+ scp.setRegistration(null);
+ reg.unregister();
+ } catch (IllegalStateException e) {
+ // Service is already unregistered do nothing
+ Activator.log.warning("InstanceProcess.disposeInstances(): registration for component " + scp.name + " is already diposed!", null);
+ }
+ } else {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10009, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.disposeInstances():
+ // cannot find registrations for " + scp.name, null);
+ }
+ }
+ scp.dispose();
+ }
+ }
+
+ /**
+ * Register the Component Factory
+ *
+ * @param scp
+ */
+ private void registerComponentFactory(ServiceComponentProp scp) {
+ if (factoryRegistrations.get(scp) != null) {
+ //the service factory is already registered
+ return;
+ }
+ ComponentFactory factory = new ComponentFactoryImpl(scp);
+ ServiceComponent sc = scp.serviceComponent;
+ BundleContext bc = scp.bc;
+ // if the factory attribute is set on the component element then
+ // register a
+ // component factory service
+ // for the Service Component on behalf of the Service Component.
+ Hashtable properties = new Hashtable(2);
+ properties.put(ComponentConstants.COMPONENT_NAME, sc.name);
+ properties.put(ComponentConstants.COMPONENT_FACTORY, sc.factory);
+ ServiceRegistration reg = bc.registerService(ComponentFactory.class.getName(), factory, properties);
+ factoryRegistrations.put(scp, reg);
+ }
+
+ /**
+ * Called by dispatcher ( Resolver) when work available on queue
+ *
+ * @param refList
+ * Map of ReferenceDescription:subtable Subtable Maps scp:service
+ * object
+ */
+ final void dynamicBind(Vector refList) {
+ if (refList == null || refList.isEmpty()) {
+ return;
+ }
+ for (int i = 0; i < refList.size(); i++) {
+ Reference ref = (Reference) refList.elementAt(i);
+ ServiceComponentProp scp = ref.scp;
+
+ Vector instances = scp.instances;
+ if (instances != null) {
+ for (int j = 0; j < instances.size(); j++) {
+ ComponentInstance compInstance = (ComponentInstance) instances.elementAt(j);
+ if (compInstance != null) {
+ try {
+ scp.bindReference(ref, compInstance);
+ } catch (Exception ex) {
+ // ex.printStackTrace();
+ }
+ }
+ }
+ } else {
+ // the component is not used and therefore it is not yet
+ // instantiated!
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10012, scp.name, null, false);
+ // //Activator.log.debug("InstanceProcess.dynamicBind():
+ // null instances! for component " + scp.name, null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by dispatcher ( Resolver) when work available on queue
+ *
+ * @param serviceTable
+ * Map of ReferenceDescription:subtable Subtable Maps scp:service
+ * object
+ */
+ final void dynamicUnBind(Hashtable serviceTable) {
+ try {
+
+ if (serviceTable == null || serviceTable.isEmpty()) {
+ return;
+ }
+ // for each element in the table
+ Enumeration e = serviceTable.keys();
+ while (e.hasMoreElements()) {
+ Reference ref = (Reference) e.nextElement();
+ Hashtable serviceSubTable = (Hashtable) serviceTable.get(ref);
+ Enumeration sub = serviceSubTable.keys();
+ while (sub.hasMoreElements()) {
+ ServiceComponentProp scp = (ServiceComponentProp) sub.nextElement();
+ ServiceReference serviceReference = (ServiceReference) serviceSubTable.get(scp);
+ // get the list of instances created
+ Vector instances = scp.instances;
+ for (int i = 0; i < instances.size(); i++) {
+ ComponentInstance compInstance = (ComponentInstance) instances.elementAt(i);
+ if (compInstance != null) {
+ try {
+ scp.unbindDynamicReference(ref, compInstance, serviceReference);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * registerService
+ *
+ * @param scp
+ * ComponentDescription plus Properties
+ * @param factory
+ * boolean
+ * @param ci
+ * the component instance created by ComponentFactoryImpl!
+ */
+ private void registerService(ServiceComponentProp scp, boolean factory, ComponentInstanceImpl ci) {
+
+ // register the service using a ServiceFactory
+ ServiceRegistration reg = null;
+ Object service;
+ if (scp.registration != null) {
+ //the service has already been registered
+ return;
+ }
+ if (factory) {
+ // register as service factory
+ service = new FactoryReg(scp);
+ } else {
+ service = new ServiceReg(scp, ci);
+ }
+ reg = scp.bc.registerService(scp.serviceComponent.provides, service, scp.getProperties());
+
+ if (Activator.DEBUG) {
+ Activator.log.debug("InstanceProcess.registerService(): " + scp.name + " registered as " + ((factory) ? "*factory*" : "*service*"), null);
+ }
+ if (scp.disposed) {
+ //must unregister the service because it was not able to unregister when the component was disposed
+ try {
+ reg.unregister();
+ if (Activator.DEBUG) {
+ Activator.log.debug("InstanceProcess.registerService(): " + scp.name + "'s service was unregistered because the component is already disposed!", null);
+ }
+ } catch (IllegalStateException e) {
+ // Service is already unregistered do nothing
+ }
+ } else {
+ scp.setRegistration(reg);
+ }
+ }
+
+ public ComponentInstanceImpl buildComponent(Bundle usingBundle, ServiceComponentProp scp, Object instance, boolean security) throws ComponentException {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10005, scp.name, null, false);
+ // //Activator.log.debug("buildInstances.buildComponent(): building
+ // component " + scp.name, null);
+ }
+ getLock();
+ Counter counter;
+ Thread curThread = Thread.currentThread();
+ synchronized (scp) {
+ Thread theSCPThread = (Thread) buildingThreads.get(scp);
+ if (theSCPThread != null && curThread != theSCPThread) { //manage cyclic calls
+ if (scp.isKindOfFactory()) {
+ // The scp is a kind of factory - multiple instances are allowed.
+ // The building of the scp is allowed
+ } else {
+ long start = System.currentTimeMillis();
+ long timeToWait = waitTime;
+ do {
+ try {
+ scp.wait(timeToWait);
+ } catch (InterruptedException ie) {
+ //do nothing
+ }
+ if (buildingThreads.get(scp) == null) {
+ //the lock is released
+ break;
+ }
+ timeToWait = waitTime + start - System.currentTimeMillis();
+ } while (timeToWait > 0);
+
+ //check if the timeout has passed or the scp is actually built
+ if (buildingThreads.get(scp) != null) {
+ freeLock();
+ // The SCP is not yet built
+ // We have two options here:
+ // 1 - Return the instance (if already created) nevertheless it is not finished its binding and activation phase
+ // 2 - throw an exception because something may have gone wrong
+ if (!scp.instances.isEmpty()) {
+ Activator.log.warning("Returning SCP instance which is not fully activated!", new Exception("Debug callstack"));
+ return (ComponentInstanceImpl) scp.instances.firstElement();
+ }
+
+ throw new RuntimeException("The instance creation of component " + scp + " took longer than " + waitTime + " ms. There might be a synchronization problem in this callstack or just the instance creation took too long!");
+ }
+ }
+ }
+ buildingThreads.put(scp, curThread);
+
+ // keep track of how many times we have re-entered this method
+ counter = (Counter) stackCounts.get(curThread);
+ if (counter == null) {
+ counter = new Counter();
+ stackCounts.put(curThread, counter);
+ }
+ counter.count++;
+ }
+
+ long start = 0l;
+ try {
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ Activator.log.info("[DS perf] Start building instance of component " + scp);
+ }
+ ComponentInstanceImpl componentInstance = null;
+ try {
+ componentInstance = scp.build(usingBundle, instance, security);
+ } catch (ComponentException e) {
+ Activator.log.error(e.getMessage(), e.getCause());
+ throw e;
+ } catch (Throwable t) {
+ Activator.log.error("[SCR] Error while building component " + scp.name, t);
+ throw new ComponentException("Error while building component " + scp.name, t);
+ } finally {
+ // keep track of how many times we have re-entered this method
+ counter.count--;
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ Activator.log.info("[DS perf] The instance of component " + scp + " is built for " + start + " ms");
+ }
+ }
+
+ // if this is the last time in this method and we have "delayed"
+ // bind actions to do (there was a circularity during bind)
+ if (counter.count == 0 && !delayedBindList.isEmpty()) {
+ // put delayed dynamic binds on the queue.
+ // (this is used to handle circularity)
+ resolver.mgr.enqueueWork(resolver, Resolver.DYNAMICBIND, delayedBindList.clone(), security);
+ delayedBindList.removeAllElements();
+ }
+
+ return componentInstance;
+ } finally {
+ synchronized (scp) {
+ if (counter.count == 0) {
+ stackCounts.remove(curThread);
+ }
+ buildingThreads.remove(scp);
+ scp.notify();
+ }
+ freeLock();
+ }
+ }
+
+ /**
+ * Acquire a service object from a {@link ServiceReference}.
+ *
+ * This method checks if "getting" the service could cause a cycle. If so,
+ * it breaks the cycle and returns null.
+ *
+ * @param reference
+ * @param serviceReference
+ *
+ * @return the service object or null if it would cause a circularity
+ */
+ public Object getService(Reference reference, ServiceReference serviceReference) {
+ // check if getting this service would cause a circularity
+ if (checkCanCauseCycle(reference, serviceReference)) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10010, reference.reference.name + " ; The service reference is " + serviceReference, null, false);
+ // //Activator.log.debug("InstanceProcess.getService(): cannot
+ // get service because of circularity! Reference is: " +
+ // reference.reference.name, null);
+ }
+ return null;
+ }
+
+ // getting this service will not cause a circularity
+ return reference.scp.bc.getService(serviceReference);
+
+ }
+
+ /**
+ * Check the "cycle list" put in the scp by the resolver to see if getting
+ * this reference would cause a circularity.
+ *
+ * A circularity is only possible if the "producer" of the service is also a
+ * service component.
+ *
+ * If getting the service could cause a circularity and the reference's
+ * policy is "dynamic", add an entry to the "delayed bind list" which is
+ * processed when the component is built
+ *
+ * @param reference
+ * @param serviceReference
+ * @return if getting the service could cause a circularity
+ */
+ private boolean checkCanCauseCycle(Reference reference, ServiceReference serviceReference) {
+
+ ServiceComponentProp consumerSCP = reference.scp;
+ // if we are not building a component, no cycles possible
+ if (buildingThreads.isEmpty()) {
+ return false;
+ }
+
+ String producerComponentName = (String) serviceReference.getProperty(ComponentConstants.COMPONENT_NAME);
+
+ // if producer is not a service component, no cycles possible
+ if (producerComponentName == null) {
+ return false;
+ }
+
+ // check if producer is the "delayed activate" list
+ if (consumerSCP.getDelayActivateSCPNames() == null || !consumerSCP.getDelayActivateSCPNames().contains(producerComponentName)) {
+ return false;
+ }
+
+ // find producer scp
+ ServiceComponentProp producerSCP = null;
+ synchronized (resolver.satisfiedSCPs) {
+ for (int i = 0; i < resolver.satisfiedSCPs.size(); i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) resolver.satisfiedSCPs.elementAt(i);
+ if (producerComponentName.equals(scp.serviceComponent.name)) {
+ // found the producer scp
+ producerSCP = scp;
+ break;
+ }
+ }
+ }
+
+ if (producerSCP != null) {
+ if (producerSCP.serviceComponent.serviceFactory) {
+ // producer is a service factory - there is a new instance for
+ // every
+ // bundle, so see if one of the instances is used by this bundle
+ if (!producerSCP.instances.isEmpty()) {
+ Bundle bundle = consumerSCP.bc.getBundle();
+ for (int i = 0; i < producerSCP.instances.size(); i++) {
+ ComponentInstanceImpl producerComponentInstance = (ComponentInstanceImpl) producerSCP.instances.elementAt(i);
+ if (producerComponentInstance.getComponentContext().getUsingBundle().equals(bundle)) {
+ // a producer already exists, so no cycle possible
+ return false;
+ }
+ }
+ }
+ } else {
+ // producer is not a service factory - there will only ever be
+ // one
+ // instance - if it exists then no cycle possible
+ if (!producerSCP.instances.isEmpty()) {
+ return false;
+ }
+ }
+ }
+
+ // producer scp is not active - do not activate it because that could
+ // cause circularity
+
+ // if reference has bind method and policy=dynamic, activate later and
+ // bind
+ if (reference.reference.bind != null && reference.policy == ComponentReference.POLICY_DYNAMIC) {
+ // delay bind by putting on the queue later
+ delayedBindList.addElement(reference);
+ }
+
+ // can't get service now because of circularity - we will bind later
+ // (dynamically) if the reference had a bind method and was dynamic
+ return true;
+ }
+
+ /**
+ * Counts re-entry in to the
+ * {@link InstanceProcess#buildComponent(Bundle, ServiceComponentProp, Object)} method.
+ * This is used to handle circular dependencies.
+ */
+ class Counter {
+ int count = 0;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Reference.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Reference.java
new file mode 100644
index 0000000..bfb5996
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Reference.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.util.Dictionary;
+import java.util.Vector;
+import org.eclipse.equinox.internal.ds.model.*;
+import org.osgi.framework.*;
+import org.osgi.service.component.ComponentConstants;
+
+/**
+ * The reference class is used only in the resolver. It is actually like
+ * "instance of a references". It is used to track available, eligible
+ * references. The reference objects relates to ServiceComponentProp as many to
+ * one.
+ *
+ * @author Valentin Valchev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public final class Reference {
+
+ public ComponentReference reference;
+ ServiceComponentProp scp;
+
+ // -- begin cache
+ String interfaceName;
+ String target;
+ int policy;
+ int cardinalityHigh;
+ int cardinalityLow;
+
+ // -- end cache
+
+ /**
+ * Reference object
+ *
+ * @param reference
+ * the component reference
+ * @param properties
+ * the *actual* properties that the component has. These are the
+ * SCP properties, e.g the default one in the XML file + the one
+ * that are configured in the CM.
+ */
+ Reference(ComponentReference reference, ServiceComponentProp scp, Dictionary properties) {
+ this.reference = reference;
+ this.scp = scp;
+ this.interfaceName = reference.interfaceName;
+ this.target = reference.target;
+
+ // RFC 80 section 5.3.1.3:
+ // If [target] is not specified and there is no <reference-name>.target
+ // component
+ // property, then the selection filter used to select the desired
+ // service is
+ // �(objectClass=�+<interface-name>+�)�.
+ if (properties != null) {
+ target = (String) properties.get(reference.name + ComponentConstants.REFERENCE_TARGET_SUFFIX);
+ }
+ if (target == null) {
+ target = "(objectClass=" + interfaceName + ")";
+ }
+
+ // If it is not specified, then a policy of �static� is used.
+ policy = reference.policy;
+
+ // Cardinality indicates the number of services, matching this
+ // reference,
+ // which will bind to this Service Component. Possible values are:
+ // 0..1, 0..n, 1..1 (i.e. exactly one), 1..n (i.e. at least one).
+ // This attribute is optional. If it is not specified, then a
+ // cardinality
+ // of �1..1� is used.
+ switch (reference.cardinality) {
+ case ComponentReference.CARDINALITY_0_1 :
+ cardinalityLow = 0;
+ cardinalityHigh = 1;
+ break;
+ case ComponentReference.CARDINALITY_0_N :
+ cardinalityLow = 0;
+ cardinalityHigh = 999999999;
+ break;
+ case ComponentReference.CARDINALITY_1_1 :
+ cardinalityLow = 1;
+ cardinalityHigh = 1;
+ break;
+ case ComponentReference.CARDINALITY_1_N :
+ cardinalityLow = 1;
+ cardinalityHigh = 999999999;
+ }
+
+ }
+
+ // used in Resolver.resolveEligible()
+ final boolean hasProviders() {
+ // check whether the component's bundle has service GET permission
+ if (System.getSecurityManager() != null && !scp.bc.getBundle().hasPermission(new ServicePermission(interfaceName, ServicePermission.GET))) {
+ return false;
+ }
+ // Get all service references for this target filter
+ try {
+ ServiceReference[] serviceReferences = null;
+ serviceReferences = scp.bc.getServiceReferences(interfaceName, target);
+ // if there is no service published that this Service
+ // ComponentReferences
+ if (serviceReferences != null) {
+ return true;
+ }
+ } catch (InvalidSyntaxException e) {
+ Activator.log.warning("Reference.hasLegacyProviders(): invalid target filter " + target, e);
+ }
+ return false;
+ }
+
+ // if the cardinality is "0..1" or "0..n" then this refernce is not required
+ final boolean isRequiredFor(ServiceComponent cd) {
+ // // we want to re-resolve if the component is static and already
+ // eligible
+ // if (policy == ComponentReference.POLICY_STATIC && cd.eligible) {
+ // return true;
+ // }
+ return cardinalityLow == 1;
+ }
+
+ final boolean isRequired() {
+ return cardinalityLow == 1;
+ }
+
+ // used in Resolver.selectDynamicBind()
+ final boolean bindNewReference(ServiceReference reference, boolean dynamicBind) {
+ if (dynamicBind) {
+ if (policy == ComponentReference.POLICY_STATIC) {
+ return false;
+ }
+ } else {
+ if (policy == ComponentReference.POLICY_DYNAMIC) {
+ return false;
+ }
+ }
+ String[] serviceNames = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+ boolean hasName = false;
+ for (int i = 0; i < serviceNames.length; i++) {
+ if (serviceNames[i].equals(interfaceName)) {
+ hasName = true;
+ break;
+ }
+ }
+ if (!hasName) {
+ return false;
+ }
+ // check target filter
+ try {
+ Filter filter = FrameworkUtil.createFilter(target);
+ if (!filter.match(reference)) {
+ return false;
+ }
+ } catch (InvalidSyntaxException e) {
+ return false;
+ }
+ if (this.reference.serviceReferences.size() < cardinalityHigh) {
+ return true;
+ }
+ return false;
+ }
+
+ // used in Resolver.selectDynamicUnBind();
+ final boolean unBindReference(BundleContext bundleContext, ServiceReference reference) {
+ // nothing dynamic to do if static
+ if (policy == ComponentReference.POLICY_STATIC) {
+ return false;
+ }
+ // now check if the Service Reference is found in the list of saved
+ // ServiceReferences
+ if (!this.reference.serviceReferences.contains(reference)) {
+ return false;
+ }
+ return true;
+ }
+
+ public ServiceComponentProp findProviderSCP(Vector scps) {
+ Filter filter;
+ try {
+ filter = FrameworkUtil.createFilter(target);
+ } catch (InvalidSyntaxException e) {
+ Activator.log.warning("Reference.findProviderSCP(): invalid target filter " + target, e);
+ return null;
+ }
+ for (int i = 0; i < scps.size(); i++) {
+ ServiceComponentProp providerSCP = (ServiceComponentProp) scps.elementAt(i);
+ if (providerSCP.serviceComponent.serviceInterfaces != null && providerSCP.serviceComponent.serviceInterfaces.contains(interfaceName)) {
+ if (filter.match(providerSCP.getProperties())) {
+ return providerSCP;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void setScp(ServiceComponentProp scp) {
+ this.scp = scp;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Resolver.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Resolver.java
new file mode 100644
index 0000000..8b16060
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/Resolver.java
@@ -0,0 +1,1058 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.util.*;
+import org.eclipse.equinox.internal.ds.model.*;
+import org.osgi.framework.*;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.ComponentException;
+
+/**
+ * Resolver.java
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.1
+ */
+
+public final class Resolver implements WorkPerformer {
+
+ // these strings are used only for debugging purpose
+ static final String[] WORK_TITLES = {"BUILD ", "DYNAMICBIND "};
+
+ /**
+ * Service Component instances need to be built.
+ */
+ public static final int BUILD = 1;
+
+ /**
+ * Service Component instances need to be rebound
+ */
+ public static final int DYNAMICBIND = 2;
+
+ /* [SC+P] enabled */
+ private Vector scpEnabled;
+
+ /**
+ * List of ComponentDescriptionProps, which are currently "satisfied"
+ * Component Configurations. this list is a subset of
+ * {@link Resolver#scpEnabled scpEnabled}.
+ */
+ public Vector satisfiedSCPs;
+
+ private InstanceProcess instanceProcess;
+
+ private Object syncLock = new Object();
+
+ public SCRManager mgr;
+
+ // TODO: Add a hashtable connecting servicereference to a list of References
+ // which they are bound to
+ // This way the search of references to unbind becomes faster when there are
+ // plenty of components.
+ // Keep in mind that build process is asynchronous.
+
+ /**
+ * Resolver constructor
+ *
+ */
+ Resolver(SCRManager mgr) {
+ scpEnabled = new Vector();
+ satisfiedSCPs = new Vector();
+ instanceProcess = new InstanceProcess(this);
+ this.mgr = mgr;
+ }
+
+ public Object getSyncLock() {
+ return syncLock;
+ }
+
+ // This method should be called when the event processing thread is blocked
+ // in a user code
+ void queueBlocked() {
+ syncLock = new Object();
+ instanceProcess = new InstanceProcess(this);
+ }
+
+ /**
+ * Clean up the SCR is shutting down
+ */
+ void dispose() {
+ Activator.log.info("Resolver.dispose()");
+ if (instanceProcess != null) {
+ instanceProcess.dispose();
+ instanceProcess = null;
+ }
+
+ scpEnabled.removeAllElements();
+ satisfiedSCPs.removeAllElements();
+ }
+
+ // -- begin *enable* component routines
+ /**
+ * enableComponents - called by the dispatchWorker
+ *
+ * @param serviceComponents -
+ * a list of all component descriptions for a single bundle to be
+ * enabled Receive ArrayList of enabled CD's from ComponentCache
+ * For each CD add to list of enabled create list of CD:CD+P
+ * create list of CD+P:ref ( where ref is a Reference Object)
+ * resolve CD+P
+ */
+ void enableComponents(Vector serviceComponents) {
+ long start = 0l;
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10062, serviceComponents != null ? serviceComponents.toString() : "null", null, false);
+ // //Activator.log.debug("Resolver.enableComponents(): " +
+ // serviceComponents, null);
+ }
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ }
+
+ synchronized (syncLock) {
+ Configuration[] configs = null;
+
+ if (serviceComponents != null) {
+ for (int i = 0; i < serviceComponents.size(); i++) {
+ ServiceComponent current = (ServiceComponent) serviceComponents.elementAt(i);
+ // don't enable components which are not marked enabled
+ // this is done here, not in the activator just because it
+ // saves a little memory
+ if (!current.enabled) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10019, current.name, null, false);
+ // //Activator.log.debug("Resolver.enableComponents():
+ // ignoring not enabled component " + current.name,
+ // null);
+ }
+ continue;
+ }
+
+ // check for a Configuration properties for this component
+ try {
+ String filter = "(|(" + Constants.SERVICE_PID + '=' + current.name + ")(" + ConfigurationAdmin.SERVICE_FACTORYPID + '=' + current.name + "))";
+ configs = ConfigurationManager.listConfigurations(filter);
+ } catch (Exception e) {
+ Activator.log.error("[SCR] Cannot list configurations for component " + current.name, e);
+ }
+ // if no Configuration
+ if (configs == null || configs.length == 0) {
+ // create ServiceComponent + Prop
+ map(current, (Dictionary) null);
+ } else {
+ // if ManagedServiceFactory
+ Configuration config = configs[0];
+ if (config.getFactoryPid() != null) {
+ // if ComponentFactory is specified
+ if (current.factory != null) {
+ Activator.log.error("[SCR - Resolver] Cannot specify both ComponentFactory and ManagedServiceFactory\n" + "The name of the ComponentFactory component is " + current.name, null);
+ continue; // skip current component
+ }
+ Activator.log.info("Resolver.enableComponents(): " + current.name + " as *managed service factory*");
+ try {
+ configs = ConfigurationManager.listConfigurations("(service.factoryPid=" + config.getFactoryPid() + ")");
+ } catch (Exception e) {
+ Activator.log.error("[SCR] Cannot list configurations for component " + current.name, e);
+ }
+ // for each MSF set of properties(P), map(CD, new
+ // CD+P(CD,P))
+ if (configs != null) {
+ for (int index = 0; index < configs.length; index++) {
+ map(current, configs[index]);
+ }
+ }
+ } else {
+ Activator.log.info("Resolver.enableComponents(): " + current.name + " as *service*");
+ // if Service, not ManagedServiceFactory
+ map(current, config);
+ }
+ } // end has configuration
+ } // end process all components!
+ }
+ }
+
+ buildNewlySatisfied();
+
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ Activator.log.info((serviceComponents != null ? "[DS perf] " + serviceComponents.size() : "[DS perf]") + " Components enabled for " + start + " ms");
+ }
+ }
+
+ protected ServiceComponentProp map(ServiceComponent component, Configuration config) {
+ Dictionary configProps = null;
+ if (config != null) {
+ try {
+ configProps = config.getProperties();
+ } catch (IllegalStateException ise) {
+ // the configuration may have beed deleted already
+ }
+ }
+ ServiceComponentProp scp = map(component, configProps);
+ if (config != null) {
+ // set the service PID & Factory Pid
+ String pid = config.getPid();
+ String fpid = config.getFactoryPid();
+ if (pid != null)
+ scp.properties.put(Constants.SERVICE_PID, pid);
+ if (fpid != null)
+ scp.properties.put(ConfigurationAdmin.SERVICE_FACTORYPID, fpid);
+ }
+ return scp;
+ }
+
+ /**
+ * Create the SCP and add to the maps
+ *
+ * @param component
+ * @param config
+ * CM configuration
+ */
+ public ServiceComponentProp map(ServiceComponent component, Dictionary configProperties) {
+ ServiceComponentProp scp = null;
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10063, component.name, null, false);
+ // //Activator.log.debug("Resolver.map(): Creating SCP for
+ // component " + component.name, null);
+ }
+ scp = new ServiceComponentProp(component, configProperties, mgr);
+
+ // Get all the required service reference descriptions for this
+ Vector referenceDescriptions = component.references;
+
+ // for each Reference Description, create a reference object
+ if (referenceDescriptions != null && !referenceDescriptions.isEmpty()) {
+ Vector references = new Vector(referenceDescriptions.size());
+
+ for (int i = 0; i < referenceDescriptions.size(); i++) {
+ // create new Reference Object and add to CD+P:ref map
+ ComponentReference cRef = (ComponentReference) referenceDescriptions.elementAt(i);
+
+ Reference ref = new Reference(cRef, scp, scp.getProperties());
+ references.addElement(ref);
+ }
+ scp.references = references;
+ }
+ component.addServiceComponentProp(scp);
+ scpEnabled.addElement(scp);
+
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ return scp;
+ }
+
+ /**
+ * Get the Eligible Components
+ *
+ * loop through CD+P list of enabled get references check if eligible if
+ * true add to eligible list send to Instance Process
+ *
+ */
+ void getEligible(ServiceEvent event) {
+ if (scpEnabled.isEmpty())
+ return; // check for any enabled configurations
+
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10020, event.toString(), null, false);
+ ////Activator.log.debug("Resolver.getEligible(): processing service event " + event, null);
+ String eventType = "";
+ if (event.getType() == ServiceEvent.UNREGISTERING) {
+ eventType = "UNREGISTERING";
+ } else if (event.getType() == ServiceEvent.REGISTERED) {
+ eventType = "REGISTERED";
+ } else if (event.getType() == ServiceEvent.MODIFIED) {
+ eventType = "MODIFIED";
+ }
+ Activator.log.debug(0, 10050, eventType, null, false);
+ }
+
+ Object target = null;
+ Vector resolvedComponents = null;
+ switch (event.getType()) {
+ case ServiceEvent.REGISTERED :
+ Vector componentsWithStaticRefs;
+ synchronized (syncLock) {
+ componentsWithStaticRefs = selectStaticBind(satisfiedSCPs, event.getServiceReference());
+ if (componentsWithStaticRefs != null) {
+ removeAll(satisfiedSCPs, componentsWithStaticRefs);
+ }
+ }
+ if (componentsWithStaticRefs != null) {
+ instanceProcess.disposeInstances(componentsWithStaticRefs);
+ }
+
+ synchronized (syncLock) {
+ resolvedComponents = resolveEligible();
+ //no need to sync here
+ target = selectDynamicBind(resolvedComponents, event.getServiceReference());
+
+ // build the newly satisfied components
+ removeAll(resolvedComponents, satisfiedSCPs);
+ if (!resolvedComponents.isEmpty()) {
+ addAll(satisfiedSCPs, resolvedComponents);
+ }
+ }
+
+ //do synchronous bind
+ if (target != null) {
+ instanceProcess.dynamicBind((Vector) target);
+ }
+
+ if (!resolvedComponents.isEmpty()) {
+ instanceProcess.buildComponents(resolvedComponents, false);
+ }
+
+ break;
+ case ServiceEvent.UNREGISTERING :
+ Vector newlyUnsatisfiedSCPs;
+ synchronized (syncLock) {
+ newlyUnsatisfiedSCPs = (Vector) satisfiedSCPs.clone();
+ removeAll(newlyUnsatisfiedSCPs, resolveEligible());
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ removeAll(satisfiedSCPs, newlyUnsatisfiedSCPs);
+ }
+ }
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ // synchronously dispose newly unsatisfied components
+ instanceProcess.disposeInstances(newlyUnsatisfiedSCPs);
+ }
+
+ Vector componentsToDispose;
+ Vector newlySatisfiedSCPs = null;
+ synchronized (syncLock) {
+ //check for components with static reference to this service
+ componentsToDispose = selectStaticUnBind(satisfiedSCPs, event.getServiceReference());
+ if (componentsToDispose != null) {
+ removeAll(satisfiedSCPs, componentsToDispose);
+ }
+ }
+ //dispose instances from staticUnbind
+ if (componentsToDispose != null) {
+ instanceProcess.disposeInstances(componentsToDispose);
+ }
+
+ synchronized (syncLock) {
+ // Pass in the set of currently resolved components, check each one -
+ // do we need to unbind
+ target = selectDynamicUnBind(satisfiedSCPs, event.getServiceReference());
+
+ if (componentsToDispose != null) {
+ // some components with static references were disposed. Try to build them again
+ // get list of newly satisfied SCPs and build them
+ newlySatisfiedSCPs = resolveEligible();
+ removeAll(newlySatisfiedSCPs, satisfiedSCPs);
+ if (!newlySatisfiedSCPs.isEmpty()) {
+ addAll(satisfiedSCPs, newlySatisfiedSCPs);
+ }
+ }
+ }
+
+ instanceProcess.dynamicUnBind((Hashtable) target); // do synchronous unbind
+
+ if (newlySatisfiedSCPs != null && !newlySatisfiedSCPs.isEmpty()) {
+ instanceProcess.buildComponents(newlySatisfiedSCPs, false);
+ }
+
+ return;
+
+ case ServiceEvent.MODIFIED :
+ synchronized (syncLock) {
+ // check for newly unsatisfied components and synchronously
+ // dispose them
+ newlyUnsatisfiedSCPs = (Vector) satisfiedSCPs.clone();
+ removeAll(newlyUnsatisfiedSCPs, resolveEligible());
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ removeAll(satisfiedSCPs, newlyUnsatisfiedSCPs);
+ }
+ }
+
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ instanceProcess.disposeInstances(newlyUnsatisfiedSCPs);
+ }
+
+ synchronized (syncLock) {
+ //check for components with static reference to this service
+ componentsToDispose = selectStaticUnBind(satisfiedSCPs, event.getServiceReference());
+ if (componentsToDispose != null) {
+ removeAll(satisfiedSCPs, componentsToDispose);
+ }
+ }
+
+ if (componentsToDispose != null) {
+ instanceProcess.disposeInstances(componentsToDispose);
+ }
+
+ synchronized (syncLock) {
+ // dynamic unbind
+ // check each satisfied scp - do we need to unbind
+ target = selectDynamicUnBind(satisfiedSCPs, event.getServiceReference());
+ }
+
+ if (target != null) {
+ instanceProcess.dynamicUnBind((Hashtable) target);
+ }
+
+ synchronized (syncLock) {
+ // dynamic bind
+ target = selectDynamicBind(satisfiedSCPs, event.getServiceReference());
+
+ // get list of newly satisfied SCPs and build them
+ newlySatisfiedSCPs = resolveEligible();
+ removeAll(newlySatisfiedSCPs, satisfiedSCPs);
+ if (!newlySatisfiedSCPs.isEmpty()) {
+ addAll(satisfiedSCPs, newlySatisfiedSCPs);
+ }
+ }
+
+ if (target != null) {
+ instanceProcess.dynamicBind((Vector) target);
+ }
+ if (!newlySatisfiedSCPs.isEmpty()) {
+ instanceProcess.buildComponents(newlySatisfiedSCPs, false);
+ }
+ }
+ }
+
+ void buildNewlySatisfied() {
+ Vector resolvedComponents;
+ synchronized (syncLock) {
+ findDependencyCycles();
+ resolvedComponents = resolveEligible();
+ removeAll(resolvedComponents, satisfiedSCPs);
+ if (!resolvedComponents.isEmpty()) {
+ addAll(satisfiedSCPs, resolvedComponents);
+ }
+ }
+
+ if (!resolvedComponents.isEmpty()) {
+ instanceProcess.buildComponents(resolvedComponents, false);
+ }
+ }
+
+ private Vector resolveEligible() {
+ try {
+ Vector enabledSCPs = (Vector) scpEnabled.clone();
+ for (int k = enabledSCPs.size() - 1; k >= 0; k--) {
+ ServiceComponentProp scp = (ServiceComponentProp) enabledSCPs.elementAt(k);
+ Vector refs = scp.references;
+ for (int i = 0; refs != null && i < refs.size(); i++) {
+ // Loop though all the references (dependencies)for a given
+ // scp. If a dependency is not met, remove it's associated
+ // scp and
+ // re-run the algorithm
+ Reference reference = (Reference) refs.elementAt(i);
+ if (reference != null) {
+ boolean resolved = !reference.isRequiredFor(scp.serviceComponent) || reference.hasProviders();
+
+ if (!resolved) {
+ if (Activator.DEBUG) {
+ Activator.log.debug("Resolver.resolveEligible(): reference '" + reference.reference.name + "' of component '" + scp.name + "' is not resolved", null);
+ }
+ enabledSCPs.removeElementAt(k);
+ break;
+ } else if (scp.disposed) {
+ scp.disposed = false;
+ }
+ }
+ }
+ // check if the bundle providing the service has permission to
+ // register the provided interface(s)
+ if (scp.serviceComponent.provides != null && System.getSecurityManager() != null) {
+ String[] provides = scp.serviceComponent.provides;
+ boolean hasPermission = true;
+ int i = 0;
+ for (; i < provides.length; i++) {
+ // make sure bundle has permission to register the
+ // service
+ try {
+ if (!scp.bc.getBundle().hasPermission(new ServicePermission(provides[i], ServicePermission.REGISTER))) {
+ hasPermission = false;
+ break;
+ }
+ } catch (IllegalStateException ise) {
+ // the bundle of the service component is
+ // uninstalled
+ // System.out.println("IllegalStateException occured
+ // while processing component "+scp);
+ // ise.printStackTrace();
+ hasPermission = false;
+ break;
+ } catch (Throwable t) {
+ // System.out.println("Exception occured processing
+ // component "+scp);
+ // t.printStackTrace();
+ hasPermission = false;
+ break;
+ }
+ }
+ if (!hasPermission) {
+ if (Activator.DEBUG) {
+ Activator.log.debug("Resolver.resolveEligible(): Cannot satisfy component '" + scp.name + "' because its bundle does not have permissions to register service with interface " + provides[i], null);
+ }
+ scpEnabled.removeElementAt(k);
+ enabledSCPs.removeElementAt(k);
+ continue;
+ }
+ }
+ }
+
+ // if (Activator.DEBUG) {
+ // Activator.log.debug(0, 10021, enabledSCPs.toString(), null,
+ // false);
+ // ////Activator.log.debug("Resolver:resolveEligible(): resolved
+ // components = " + enabledSCPs, null);
+ // }
+ return enabledSCPs;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ // -- begin *disable* component routines
+ /**
+ * Disable list of ComponentDescriptions
+ *
+ * get all CD+P's from CD:CD+P Map get instances from CD+P:list of instance
+ * (1:n) map
+ *
+ * Strip out of Map all CD+P's Continue to pull string check each Ref
+ * dependency and continue to pull out CD+P's if they become not eligible
+ * Then call Resolver to re-resolve
+ *
+ * @param componentDescriptions
+ */
+ void disableComponents(Vector componentDescriptions) {
+ long start = 0l;
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ }
+
+ ServiceComponentProp scp;
+ ServiceComponent component;
+
+ Vector removeList = null;
+
+ // Received list of CDs to disable
+ if (componentDescriptions != null) {
+ for (int i = 0; i < componentDescriptions.size(); i++) {
+ // get the first CD
+ component = (ServiceComponent) componentDescriptions.elementAt(i);
+ component.enabled = false;
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10022, component.name, null, false);
+ ////Activator.log.debug("Resolver.disableComponents()" + component.name, null);
+ }
+
+ // then get the list of SCPs for this CD
+ Vector scpList = component.componentProps;
+ if (scpList != null) {
+ for (int iter = 0; iter < scpList.size(); iter++) {
+ scp = (ServiceComponentProp) scpList.elementAt(iter);
+ if (removeList == null) {
+ removeList = new Vector();
+ }
+ removeList.addElement(scp);
+ }
+ }
+ if (removeList != null) {
+ disposeComponentConfigs(removeList);
+ removeList.removeAllElements();
+ }
+ if (component.componentProps != null) {
+ component.componentProps.removeAllElements();
+ }
+ }
+ }
+
+ Vector newlyUnsatisfiedSCPs;
+ synchronized (syncLock) {
+ // synchronously dispose newly unsatisfied components
+ newlyUnsatisfiedSCPs = (Vector) satisfiedSCPs.clone();
+ removeAll(newlyUnsatisfiedSCPs, resolveEligible());
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ removeAll(satisfiedSCPs, newlyUnsatisfiedSCPs);
+ }
+ }
+ if (!newlyUnsatisfiedSCPs.isEmpty()) {
+ instanceProcess.disposeInstances(newlyUnsatisfiedSCPs);
+ }
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ Activator.log.info("[DS perf] " + componentDescriptions.size() + " Components disabled for " + start + " ms");
+ }
+ }
+
+ public void disposeComponentConfigs(Vector scps) {
+ // unregister, deactivate, and unbind
+ synchronized (syncLock) {
+ removeAll(satisfiedSCPs, scps);
+ removeAll(scpEnabled, scps);
+ }
+ instanceProcess.disposeInstances(scps);
+ }
+
+ // -- end *service listener*
+
+ public void performWork(int workAction, Object workObject) {
+ try {
+ if (Activator.DEBUG) {
+ String work = WORK_TITLES[workAction - 1];
+ Activator.log.debug(0, 10023, work + workObject, null, false);
+ ////Activator.log.debug("Resolver.dispatchWork(): " + work + workObject, null);
+ }
+ switch (workAction) {
+ case BUILD :
+ if (workObject != null) {
+ Vector queue = (Vector) workObject;
+ synchronized (syncLock) {
+ // remove unsatisfied configs
+ for (int i = queue.size() - 1; i >= 0; i--) {
+ if (!satisfiedSCPs.contains(queue.elementAt(i))) {
+ //System.out.println("-----BUILD: removing "+queue.elementAt(i));
+ queue.removeElementAt(i);
+ }
+ }
+ if (queue.isEmpty())
+ return;
+ }
+ instanceProcess.buildComponents(queue, false);
+
+ // dispose configs that were already tried to dispose while building
+ Vector toDispose = null;
+ synchronized (syncLock) {
+ for (int i = queue.size() - 1; i >= 0; i--) {
+ if (!satisfiedSCPs.contains(queue.elementAt(i))) {
+ //System.out.println("-----DISPOSE after BUILD: removing "+queue.elementAt(i));
+ if (toDispose == null) {
+ toDispose = new Vector(2);
+ }
+ toDispose.addElement(queue.elementAt(i));
+ }
+ }
+ if (toDispose == null)
+ return; //nothing to dispose
+ }
+ instanceProcess.disposeInstances(toDispose);
+ }
+ break;
+ case DYNAMICBIND :
+ if (workObject != null) {
+ Vector toBind = (Vector) workObject;
+ synchronized (syncLock) {
+ // remove unsatisfied configs
+ for (int i = toBind.size() - 1; i >= 0; i--) {
+ Reference ref = (Reference) toBind.elementAt(i);
+ if (!satisfiedSCPs.contains(ref.scp)) {
+ //System.out.println("--BIND: removing "+ref.scp);
+ toBind.removeElementAt(i);
+ }
+ }
+ if (toBind.isEmpty())
+ return;
+ }
+ instanceProcess.dynamicBind(toBind);
+
+ }
+ break;
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Vector selectDynamicBind(Vector scps, ServiceReference serviceReference) {
+ try {
+ Vector toBind = null;
+ for (int i = 0, size = scps.size(); i < size; i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
+ // if it is not already eligible it will bind with the static
+ // scps
+ Vector references = scp.references;
+ // it is absolutely legal component if it doesn't contains
+ // references!
+ if (references != null) {
+ for (int j = 0; j < references.size(); j++) {
+ Reference reference = (Reference) references.elementAt(j);
+ if (reference.bindNewReference(serviceReference, true)) {
+ if (toBind == null) {
+ toBind = new Vector(2);
+ }
+ toBind.addElement(reference);
+ }
+ }
+ }
+ }
+ if (toBind != null && Activator.DEBUG) {
+ Activator.log.debug(0, 10025, toBind.toString(), null, false);
+ // //Activator.log.debug("Resolver.selectDynamicBind(): selected
+ // = " + bindTable, null);
+ }
+ return toBind;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ private Vector selectStaticBind(Vector scps, ServiceReference serviceReference) {
+ try {
+ Vector toBind = null;
+ for (int i = 0, size = scps.size(); i < size; i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
+ // if it is not already eligible it will bind with the static
+ // scps
+ Vector references = scp.references;
+ if (references != null) {
+ for (int j = 0; j < references.size(); j++) {
+ Reference reference = (Reference) references.elementAt(j);
+ if (reference.bindNewReference(serviceReference, false)) {
+ if (toBind == null) {
+ toBind = new Vector(2);
+ }
+ toBind.addElement(scp);
+ break;
+ }
+ }
+ }
+ }
+ if (toBind != null && Activator.DEBUG) {
+ Activator.log.debug(0, 10061, toBind.toString(), null, false);
+ // //Activator.log.debug("Resolver.selectStaticBind(): selected
+ // = " + toBind, null);
+ }
+ return toBind;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ private Vector selectStaticUnBind(Vector scpsToCheck, ServiceReference serviceReference) {
+ try {
+ Vector toUnbind = null;
+ for (int i = 0, size = scpsToCheck.size(); i < size; i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) scpsToCheck.elementAt(i);
+ // if it is not already eligible it will bind with the static
+ // scps
+ Vector references = scp.references;
+ // it is absolutely legal component if it doesn't contains
+ // references!
+ if (references != null) {
+ for (int j = 0; j < references.size(); j++) {
+ Reference reference = (Reference) references.elementAt(j);
+ if (reference.policy == ComponentReference.POLICY_STATIC && reference.reference.serviceReferences.contains(serviceReference)) {
+
+ if (toUnbind == null) {
+ toUnbind = new Vector(2);
+ }
+ toUnbind.addElement(scp);
+ }
+ }
+ }
+ }
+ if (toUnbind != null)
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10060, toUnbind.toString(), null, false);
+ // //Activator.log.debug("Resolver.selectStaticUnBind():
+ // selected = " + toUnbind, null);
+ }
+ return toUnbind;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * selectDynamicUnBind Determine which resolved component description with
+ * properties need to unbind from this unregistering service Return map of
+ * reference description and component description with properties, for
+ * each.
+ *
+ * @param scps
+ * @param serviceReference
+ * @return this is fairly complex to explain ;(
+ */
+ private Hashtable selectDynamicUnBind(Vector scps, ServiceReference serviceReference) {
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10026, null, null, false);
+ // //Activator.log.debug("Resolver.selectDynamicUnBind():
+ // entered", null);
+ }
+ Hashtable unbindTable = null; // ReferenceDescription:subTable
+ Hashtable unbindSubTable = null; // scp:sr
+ for (int i = 0; i < scps.size(); i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) scps.elementAt(i);
+
+ BundleContext bc = scp.bc;
+ Vector references = scp.references;
+ // some components may not contain references and it is
+ // absolutely valid
+ if (references != null) {
+ for (int j = 0; j < references.size(); j++) {
+ Reference reference = (Reference) references.elementAt(j);
+ // Does the scp require this service, use the Reference
+ // object to check
+ if (reference.unBindReference(bc, serviceReference)) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10027, scp.toString(), null, false);
+ // //Activator.log.debug("Resolver.selectDynamicUnBind():
+ // unbinding " + scp, null);
+ }
+ if (unbindSubTable == null) {
+ unbindSubTable = new Hashtable(11);
+ }
+ unbindSubTable.put(scp, serviceReference);
+ if (unbindTable == null) {
+ unbindTable = new Hashtable(11);
+ }
+ unbindTable.put(reference, unbindSubTable);
+ } else {
+ if (Activator.DEBUG) {
+ Activator.log.debug("Resolver.selectDynamicUnBind(): not unbinding " + scp + " service ref=" + serviceReference, null);
+ }
+ }
+ }
+ }
+ }
+ if (unbindTable != null && Activator.DEBUG) {
+ Activator.log.debug(0, 10028, unbindTable.toString(), null, false);
+ // //Activator.log.debug("Resolver.selectDynamicUnBind():
+ // unbindTable is " + unbindTable, null);
+ }
+ return unbindTable;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ // used by the ComponentFactoryImpl to build new Component configurations
+ public ServiceComponentProp mapNewFactoryComponent(ServiceComponent component, Dictionary configProperties) {
+ synchronized (syncLock) {
+ // create a new scp (adds to resolver enabledSCPs list)
+ ServiceComponentProp newSCP = map(component, configProperties);
+ newSCP.setComponentFactory(false); // avoid registration of new
+ // ComponentFactory
+
+ // we added a SCP, so check for circularity and mark cycles
+ findDependencyCycles();
+
+ // get list of newly satisfied SCPs and check whether the new SCP is
+ // satisfied
+ Vector newlySatisfiedSCPs = resolveEligible();
+ removeAll(newlySatisfiedSCPs, satisfiedSCPs);
+ if (!newlySatisfiedSCPs.contains(newSCP)) {
+ scpEnabled.removeElement(newSCP);
+ throw new ComponentException("Cannot resolve instance of " + newSCP + " with properties " + configProperties);
+ }
+ satisfiedSCPs.addElement(newSCP);
+
+ return newSCP;
+ }
+ }
+
+ /**
+ * Check through the enabled list for cycles. Cycles can only exist if every
+ * service is provided by a Service Component (not legacy OSGi). If the
+ * cycle has no optional dependencies, log an error and disable a Component
+ * Configuration in the cycle. If cycle can be "broken" by an optional
+ * dependency, make a note (stored in the
+ * {@link ServiceComponentProp#delayActivateSCPNames} Vector).
+ *
+ * @throws CircularityException
+ * if cycle exists with no optional dependencies
+ */
+ private void findDependencyCycles() {
+ Vector emptyVector = new Vector();
+ try {
+ // find the SCPs that resolve using other SCPs and record their
+ // dependencies
+ Hashtable dependencies = new Hashtable();
+
+ for (int i = scpEnabled.size() - 1; i >= 0; i--) {
+ ServiceComponentProp enabledSCP = (ServiceComponentProp) scpEnabled.elementAt(i);
+ if (enabledSCP.references != null) {
+ Vector dependencyVector = new Vector(1);
+ for (int j = 0; j < enabledSCP.references.size(); j++) {
+ Reference reference = (Reference) enabledSCP.references.elementAt(j);
+
+ // see if it resolves to one of the other enabled SCPs
+ ServiceComponentProp providerSCP = reference.findProviderSCP(scpEnabled);
+ if (providerSCP != null) {
+ dependencyVector.addElement(new ReferenceSCPWrapper(reference, providerSCP));
+ }
+ } // end for
+
+ if (!dependencyVector.isEmpty()) {
+ // SCP resolves using some other SCPs, could be a cycle
+ dependencies.put(enabledSCP, dependencyVector);
+ } else {
+ dependencies.put(enabledSCP, emptyVector);
+ }
+ }
+ } // end for
+
+ // traverse dependency tree and look for cycles
+ Hashtable visited = new Hashtable(11);
+ Enumeration keys = dependencies.keys();
+
+ while (keys.hasMoreElements()) {
+ ServiceComponentProp scp = (ServiceComponentProp) keys.nextElement();
+ if (!visited.containsKey(scp)) {
+ Vector currentStack = new Vector(2);
+ checkDependencies(scp, visited, dependencies, currentStack);
+ }
+ }
+ } catch (CircularityException e) {
+ Activator.log.error("[SCR] Circularity Exception found for component " + e.getCausingComponent(), e);
+ // disable offending SCP
+ scpEnabled.removeElement(e.getCausingComponent());
+ // try again
+ findDependencyCycles();
+ }
+ }
+
+ /**
+ * Recursively do a depth-first traversal of a dependency tree, looking for
+ * cycles.
+ * <p>
+ * If a cycle is found, calls
+ * {@link Resolver#processDependencyCycle(ReferenceSCPWrapper, Vector)}.
+ * </p>
+ *
+ * @param scp
+ * current node in dependency tree
+ * @param visited
+ * holdes the visited nodes
+ * @param dependencies
+ * Dependency tree - a Hashtable of ({@link ServiceComponentProp}):(Vector
+ * of {@link ReferenceSCPWrapper}s)
+ * @param currentStack
+ * Vector of {@link ReferenceSCPWrapper}s - the history of our
+ * traversal so far (the path back to the root of the tree)
+ * @throws CircularityException
+ * if an cycle with no optional dependencies is found.
+ */
+ private void checkDependencies(ServiceComponentProp scp, Hashtable visited, Hashtable dependencies, Vector currentStack) throws CircularityException {
+
+ // the component has already been visited and it's dependencies checked
+ // for cycles
+ if (visited.containsKey(scp)) {
+ return;
+ }
+
+ Vector refSCPs = (Vector) dependencies.get(scp);
+ if (refSCPs != null) {
+ for (int i = 0; i < refSCPs.size(); i++) {
+ ReferenceSCPWrapper refSCP = (ReferenceSCPWrapper) refSCPs.elementAt(i);
+ if (currentStack.contains(refSCP)) {
+ // may throw circularity exception
+ processDependencyCycle(refSCP, currentStack);
+ return;
+ }
+ currentStack.addElement(refSCP);
+
+ checkDependencies(refSCP.producer, visited, dependencies, currentStack);
+
+ currentStack.removeElement(refSCP);
+ }
+ }
+ visited.put(scp, "");
+ }
+
+ /**
+ * A cycle was detected. SCP is referenced by the last element in
+ * currentStack. Throws CircularityException if the cycle does not contain
+ * an optional dependency, else choses a point at which to "break" the cycle
+ * (the break point must be immediately after an optional dependency) and
+ * adds a "cycle note".
+ *
+ * @see ServiceComponentProp#delayActivateSCPNames
+ */
+ private void processDependencyCycle(ReferenceSCPWrapper refSCP, Vector currentStack) throws CircularityException {
+ // find an optional dependency
+ ReferenceSCPWrapper optionalRefSCP = null;
+ for (int i = currentStack.indexOf(refSCP); i < currentStack.size(); i++) {
+ ReferenceSCPWrapper cycleRefSCP = (ReferenceSCPWrapper) currentStack.elementAt(i);
+ if (!cycleRefSCP.ref.isRequired()) {
+ optionalRefSCP = cycleRefSCP;
+ break;
+ }
+ }
+ if (optionalRefSCP == null) {
+ throw new CircularityException(refSCP.ref.scp);
+ }
+ // check whether the optional reference is static - this is not allowed
+ // because of the way components with static refereces are built
+ if (optionalRefSCP.ref.policy == ComponentReference.POLICY_STATIC) {
+ Activator.log.error("[SCR] Static optional reference detected in a component cycle " + "and it will be removed.The referece is " + optionalRefSCP.ref.reference, null);
+
+ optionalRefSCP.ref.scp.references.removeElement(optionalRefSCP.ref);
+ }
+
+ // the dependent component will be processed with delay whenever
+ // necessary
+ optionalRefSCP.ref.scp.setDelayActivateSCPName(optionalRefSCP.producer.serviceComponent.name);
+ }
+
+ // used to remove all elements of vector which occur in another vector
+ private void removeAll(Vector src, Vector elementsToRemove) {
+ for (int i = src.size() - 1; i >= 0; i--) {
+ if (elementsToRemove.contains(src.elementAt(i))) {
+ src.removeElementAt(i);
+ }
+ }
+ }
+
+ // used to add all elements of vector in another vector
+ private void addAll(Vector src, Vector elementsToAdd) {
+ for (int i = 0; i < elementsToAdd.size(); i++) {
+ if (!src.contains(elementsToAdd.elementAt(i))) {
+ src.addElement(elementsToAdd.elementAt(i));
+ }
+ }
+ }
+
+ public void removeFromSatisfiedList(ServiceComponentProp scp) {
+ synchronized (syncLock) {
+ satisfiedSCPs.remove(scp);
+ }
+ }
+
+ /**
+ * Used to traverse the dependency tree in order to find cycles.
+ *
+ */
+ private class ReferenceSCPWrapper {
+ public Reference ref;
+ public ServiceComponentProp producer;
+
+ protected ReferenceSCPWrapper(Reference ref, ServiceComponentProp producer) {
+ this.ref = ref;
+ this.producer = producer;
+ }
+
+ public String toString() {
+ return "Reference : " + ref + " ::: SCP : " + producer;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRManager.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRManager.java
new file mode 100644
index 0000000..770a36e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRManager.java
@@ -0,0 +1,638 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import org.eclipse.equinox.internal.ds.model.ServiceComponent;
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+import org.eclipse.equinox.internal.util.event.Queue;
+import org.eclipse.equinox.internal.util.ref.Log;
+import org.eclipse.equinox.internal.util.threadpool.ThreadPoolManager;
+import org.osgi.framework.*;
+import org.osgi.service.cm.*;
+import org.osgi.service.component.ComponentConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Manager of update and delete events, forwarded by ConfigurationImpl to the
+ * corresponding ManagedService(Factories). As those events are asynchronuos, a
+ * separate thread is engaged for their execution.
+ *
+ * @author Maria Ivanova
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.2
+ */
+
+public class SCRManager implements ServiceListener, SynchronousBundleListener, ConfigurationListener, WorkPerformer, PrivilegedAction {
+
+ private Hashtable bundleToServiceComponents;
+ public BundleContext bc;
+ protected Queue queue;
+ public static Log log;
+ private Resolver resolver;
+
+ private WorkThread workThread;
+ protected boolean running = false;
+ protected boolean stopped = false;
+ private ServiceTracker threadPoolManagerTracker;
+ private boolean hasRegisteredServiceListener = false;
+
+ /** work action type */
+ public final int ENABLE_COMPONENTS = 1;
+ public final int DISABLE_COMPONENTS = 2;
+
+ private ComponentStorage storage;
+
+ /**
+ * Constructs the SCRManager.
+ */
+ public SCRManager(BundleContext bc, Log log) {
+ this.bc = bc;
+ SCRManager.log = log;
+
+ security = Log.security();
+
+ hasRegisteredServiceListener = true;
+ queue = new Queue(10);
+ if (Activator.startup)
+ Activator.timeLog(110); /* 110 = "Queue instantiated for " */
+
+ threadPoolManagerTracker = new ServiceTracker(bc, ThreadPoolManager.class.getName(), null);
+ threadPoolManagerTracker.open();
+ if (Activator.startup)
+ Activator.timeLog(111);
+ /*111 = "Threadpool service tracker opened for "*/
+
+ resolver = new Resolver(this);
+ if (Activator.startup)
+ Activator.timeLog(112); /* 112 = "Resolver instantiated for " */
+
+ bc.addBundleListener(this);
+ if (Activator.startup)
+ Activator.timeLog(105); /* 105 = "addBundleListener() method took " */
+
+ String storageClass = bc.getProperty("scr.storage.class");
+ if (storageClass == null) {
+ storageClass = "org.eclipse.equinox.internal.ds.storage.file.FileStorage";
+ }
+ try {
+ storage = (ComponentStorage) Class.forName(storageClass).getConstructor(new Class[] {BundleContext.class}).newInstance(new Object[] {bc});
+ } catch (Exception e) {
+ log.error("[SCR - SCRManager] could not create instance for " + storageClass, e);
+ }
+ }
+
+ public void startIt() {
+ // loop through the currently installed bundles
+ Bundle[] bundles = bc.getBundles();
+ if (bundles != null) {
+ for (int i = 0; i < bundles.length; i++) {
+ Bundle current = bundles[i];
+ // try to process the active ones.
+ if (current.getState() == Bundle.ACTIVE) {
+ startedBundle(current);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add an event to the queue. The event will be forwarded to target service
+ * as soon as possible.
+ *
+ * @param upEv
+ * event, holding info for update/deletion of a configuration.
+ */
+ public void addEvent(Object upEv, boolean security) {
+ try {
+ synchronized (queue) {
+ queue.put(upEv);
+ if (!running) {
+ if (queue.size() > 0) {
+ running = true;
+ workThread = new WorkThread(this);
+ if (security) {
+ AccessController.doPrivileged(this);
+ return;
+ }
+ ThreadPoolManager threadPool = (ThreadPoolManager) threadPoolManagerTracker.getService();
+ if (threadPool != null) {
+ threadPool.execute(workThread, Thread.MAX_PRIORITY, "Component Resolve Thread");
+ } else {
+ new Thread(workThread, "Component Resolve Thread").start();
+ }
+ }
+ } else if (workThread.waiting > 0) {
+ queue.notifyAll();
+ }
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ static boolean security = false;
+
+ public Object run() {
+ ThreadPoolManager threadPool = (ThreadPoolManager) threadPoolManagerTracker.getService();
+ if (threadPool != null) {
+ threadPool.execute(workThread, Thread.MAX_PRIORITY, "Component Resolve Thread");
+ } else {
+ new Thread(workThread, "Component Resolve Thread").start();
+ }
+ return null;
+ }
+
+ public void queueBlocked() {
+ resolver.queueBlocked();
+ synchronized (queue) {
+ running = false;
+ addEvent(null, security); // will result in starting new
+ // WorkThread to process the queued work
+ }
+ }
+
+ /**
+ * This methods takes the input parameters and creates a Queued object and
+ * queues it. The thread is notified.
+ *
+ * @param d
+ * Dispatcher for this item
+ * @param a
+ * Action for this item
+ * @param o
+ * Object for this item
+ */
+ public void enqueueWork(WorkPerformer d, int a, Object o, boolean security) {
+ addEvent(new QueuedJob(d, a, o), security);
+ }
+
+ /**
+ * Stops this thread, making it getting out of method run.
+ */
+ public void stopIt() {
+ stopped = true;
+ disposeBundles();
+ if (queue != null) {
+ queue.clear();
+ }
+ if (running) {
+ synchronized (queue) {
+ queue.notify();
+ }
+ int counter = 0;
+
+ while (running && counter < 20) {
+ // wait maximum 2 seconds to complete current task in the queue
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ie) {
+ }
+ counter++;
+ }
+ }
+ stopped = true;
+ threadPoolManagerTracker.close();
+ storage.stop();
+ }
+
+ public void serviceChanged(ServiceEvent sEv) {
+ resolver.getEligible(sEv);
+ }
+
+ public final void bundleChanged(BundleEvent event) {
+ long start = 0l;
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ log.info("[DS perf] Started processing bundle event " + event);
+ }
+ int type = event.getType();
+ if (type == BundleEvent.STOPPING) {
+ stoppingBundle(event.getBundle());
+ } else if (type == BundleEvent.STARTED) {
+ startedBundle(event.getBundle());
+ } else if (type == BundleEvent.UNINSTALLED && Activator.DBSTORE) {
+ try {
+ storage.deleteComponentDefinitions(event.getBundle().getBundleId());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ log.info("[DS perf] Processed bundle event '" + event + "' for " + start + " ms");
+ }
+ }
+
+ // -- begin 'CM listener'
+ /**
+ * Listen for configuration changes
+ *
+ * Service Components can receive properties from the Configuration Admin
+ * service. If a Service Component is activated and it�s properties are
+ * updated in the Configuration Admin service, the SCR must deactivate the
+ * component and activate the component again using the new properties.
+ *
+ * @param event
+ * ConfigurationEvent
+ */
+ public void configurationEvent(ConfigurationEvent event) {
+ if (bundleToServiceComponents != null && !bundleToServiceComponents.isEmpty()) {
+ addEvent(event, true);
+ }
+ }
+
+ protected void processConfigurationEvent(ConfigurationEvent event) {
+ if (bundleToServiceComponents == null || bundleToServiceComponents.isEmpty()) {
+ // no components found till now
+ return;
+ }
+ long start = 0l;
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug(" Resolver.configurationEvent(): pid = " + event.getPid() + ", fpid = " + event.getFactoryPid(), null);
+ }
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ log.info("[DS perf] Started processing configuration event " + event);
+ }
+
+ String pid = event.getPid();
+ String fpid = event.getFactoryPid();
+ for (Enumeration keys = bundleToServiceComponents.keys(); keys.hasMoreElements();) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10013, null, null, false);
+ // //Activator.log.debug(" hasNext", null);
+ }
+ Vector bundleComps = (Vector) bundleToServiceComponents.get(keys.nextElement());
+ // bundleComps may be null since bundleToServiceComponents
+ // may have been modified by another thread
+ if (bundleComps != null) {
+ for (int i = 0; i < bundleComps.size(); i++) {
+ ServiceComponent sc = (ServiceComponent) bundleComps.elementAt(i);
+ String name = sc.name;
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10014, name, null, false);
+ // //Activator.log.debug(" component name " + name,
+ // null);
+ }
+ if (name.equals(pid) || name.equals(fpid)) {
+ if (name.equals(fpid) && sc.factory != null) {
+ Activator.log.error("[SCR - SCRManager] ComponentFactory " + name + " cannot be managed using factory configuration!", null);
+ return;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10015, pid, null, false);
+ // //Activator.log.debug("
+ // Resolver.configurationEvent(): found
+ // component - " + pid, null);
+ }
+ processConfigurationEvent(event, sc);
+ return;
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ Activator.log.error("[SCR] Error while processing configuration event for " + event.getReference().getBundle(), e);
+ } finally {
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ log.info("[DS perf] Processed configuration event '" + event + "' for " + start + " ms");
+ }
+ }
+ }
+
+ private void processConfigurationEvent(ConfigurationEvent event, ServiceComponent sc) {
+ Configuration[] config = null;
+
+ String pid = event.getPid();
+ String fpid = event.getFactoryPid();
+
+ switch (event.getType()) {
+ case ConfigurationEvent.CM_UPDATED :
+
+ String filter = (fpid != null ? "(&" : "") + "(" + Constants.SERVICE_PID + "=" + pid + ")" + (fpid != null ? ("(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + fpid + "))") : "");
+ try {
+ config = ConfigurationManager.listConfigurations(filter);
+ } catch (IOException e) {
+ log.error("Error while listing CM Configurations", e);
+ } catch (InvalidSyntaxException e) {
+ log.error("Error while listing CM Configurations", e);
+ }
+
+ if (config == null) {
+ // The configuration may have been deleted by the now
+ // If it does not exist we must do nothing
+ return;
+ }
+
+ // if NOT a factory
+ if (fpid == null) {
+ // there is only one SCP for this SC, so we can disable the SC
+ Vector components = new Vector();
+ components.addElement(sc);
+ resolver.disableComponents(components);
+
+ // now re-enable the SC - the resolver will pick up the new
+ // config
+ sc.enabled = true;
+ resolver.enableComponents(components);
+
+ // If a MSF
+ // create a new SCP or update an existing one
+ } else {
+
+ // get scp with this PID
+ ServiceComponentProp scp = sc.getComponentPropByPID(pid);
+
+ // if only the no-props scp exists, replace it
+ if (scp == null && sc.componentProps != null && sc.componentProps.size() == 1 && (((ServiceComponentProp) sc.componentProps.elementAt(0)).getProperties().get(Constants.SERVICE_PID) == null)) {
+ scp = (ServiceComponentProp) sc.componentProps.elementAt(0);
+ }
+ // if old scp exists, dispose of it
+ if (scp != null) {
+ // config already exists - dispose of it
+ sc.componentProps.removeElement(scp);
+ Vector components = new Vector();
+ components.addElement(scp);
+ resolver.disposeComponentConfigs(components);
+ }
+
+ // create a new scp (adds to resolver enabledSCPs list)
+ resolver.map(sc, config[0]);
+
+ // kick the resolver to figure out if SCP is satisfied, etc
+ resolver.enableComponents(null);
+ }
+
+ break;
+ case ConfigurationEvent.CM_DELETED :
+
+ // if not a factory
+ if (fpid == null) {
+
+ // there is only one SCP for this SC, so we can disable the SC
+ Vector components = new Vector();
+ components.addElement(sc);
+ resolver.disableComponents(components);
+
+ // now re-enable the SC - the resolver will create SCP with
+ // no configAdmin properties
+ sc.enabled = true;
+ resolver.enableComponents(components);
+ } else {
+ // config is a factory
+
+ // get SCP created for this config (with this PID)
+ ServiceComponentProp scp = sc.getComponentPropByPID(pid);
+
+ // if this was the last SCP created for this factory
+ if (sc.componentProps.size() == 1) {
+ // there is only one SCP for this SC, so we can disable the
+ // SC
+ Vector components = new Vector();
+ components.addElement(sc);
+ resolver.disableComponents(components);
+ // now re-enable the SC - the resolver will create SCP
+ // with no configAdmin properties
+ sc.enabled = true;
+ resolver.enableComponents(components);
+ } else {
+ // we can just dispose this SCP
+ sc.componentProps.removeElement(scp);
+ Vector components = new Vector();
+ components.addElement(scp);
+ resolver.disposeComponentConfigs(components);
+ }
+ }
+ break;
+ }
+ }
+
+ private void disposeBundles() {
+ log.info("disposeBundles()");
+ // dispose ALL bundles
+ if (bundleToServiceComponents != null) {
+ for (Enumeration e = bundleToServiceComponents.keys(); e.hasMoreElements();) {
+ Bundle bundle = (Bundle) e.nextElement();
+ stoppingBundle(bundle);
+ }
+ bundleToServiceComponents.clear();
+ bundleToServiceComponents = null;
+ }
+ }
+
+ void stoppingBundle(Bundle bundle) {
+ if (bundleToServiceComponents != null) {
+ Vector components = (Vector) bundleToServiceComponents.remove(bundle);
+ // disable the components which the bundle provides
+ if (components != null) {
+ if (Activator.DEBUG) {
+ String bundleName = bundle.getSymbolicName();
+ bundleName = (bundleName == null || "".equals(bundleName)) ? bundle.getLocation() : bundleName;
+ log.debug(0, 10016, bundleName, null, false);
+ // //log.debug("SCRManager.stoppingBundle(" + bundleName
+ // + ')', null);
+ }
+ resolver.disableComponents(components);
+ if (bundleToServiceComponents.size() == 0) {
+ hasRegisteredServiceListener = false;
+ bc.removeServiceListener(this);
+ }
+ }
+ }
+ }
+
+ synchronized void startedBundle(Bundle bundle) {
+ long start = 0l;
+ if (Activator.PERF) {
+ start = System.currentTimeMillis();
+ }
+ if (bundleToServiceComponents != null) {
+ if (bundleToServiceComponents.get(bundle) != null) {
+ // the bundle is already processed - skipping it
+ return;
+ }
+ }
+
+ Dictionary allHeaders = bundle.getHeaders();
+
+ if (!((allHeaders.get(ComponentConstants.SERVICE_COMPONENT)) != null)) {
+ // no component descriptions in this bundle
+ return;
+ }
+
+ Vector components = storage.loadComponentDefinitions(bundle.getBundleId());
+ if (components != null && !components.isEmpty()) {
+ if (!hasRegisteredServiceListener) {
+ hasRegisteredServiceListener = true;
+ bc.addServiceListener(this);
+ }
+ if (Activator.PERF) {
+ start = System.currentTimeMillis() - start;
+ log.info("[DS perf] The components for bundle " + bundle + " are parsed for " + start + " ms");
+ }
+ // store the components in the cache
+ if (bundleToServiceComponents == null) {
+ bundleToServiceComponents = new Hashtable(10);
+ }
+ bundleToServiceComponents.put(bundle, components);
+ for (int i = 0; i < components.size(); i++) {
+ ServiceComponent comp = (ServiceComponent) components.elementAt(i);
+ if (comp.autoenable) {
+ comp.enabled = true;
+ }
+ }
+ // this will also resolve the component dependencies!
+ enqueueWork(this, ENABLE_COMPONENTS, components, false);
+ }
+ }
+
+ public void enableComponent(String name, Bundle bundle) {
+ changeComponent(name, bundle, true);
+ }
+
+ private void changeComponent(String name, Bundle bundle, boolean enable) {
+ try {
+ Vector componentsToProcess = null;
+
+ if (Activator.DEBUG) {
+ String message = (enable ? "SCRManager.enableComponent(): " : "SCRManager.disableComponent(): ").concat(name != null ? name : "*all*") + " from bundle " + bundle.getSymbolicName();
+ Activator.log.debug(message, null);
+ }
+ if (bundleToServiceComponents == null) {
+ // already disposed!
+ return;
+ }
+ Vector bundleComponents = (Vector) bundleToServiceComponents.get(bundle);
+ if (bundleComponents != null) {
+ if (name != null) {
+ for (int i = 0; i < bundleComponents.size(); i++) {
+ ServiceComponent component = (ServiceComponent) bundleComponents.elementAt(i);
+ if (component.name.equals(name) && component.enabled != enable) {
+ component.enabled = enable;
+ componentsToProcess = new Vector(2);
+ componentsToProcess.addElement(component);
+ break;
+ }
+ }
+ } else {
+ if (enable) {
+ // processing null parameter should be done only when
+ // enabling components
+ ServiceComponent sc;
+ componentsToProcess = new Vector();
+ for (int i = 0; i < bundleComponents.size(); i++) {
+ sc = ((ServiceComponent) bundleComponents.elementAt(i));
+ if (!sc.enabled) {
+ componentsToProcess.addElement(sc);
+ sc.enabled = enable;
+ }
+ }
+ } else {
+ Activator.log.warning("[SCRManager] Cannot dispose all components of a bundle at once!", null);
+ }
+ }
+
+ }
+ // publish to resolver the list of SCs to enable
+ if (componentsToProcess != null && !componentsToProcess.isEmpty()) {
+ if (enable) {
+ enqueueWork(this, ENABLE_COMPONENTS, componentsToProcess, security);
+ } else {
+ enqueueWork(this, DISABLE_COMPONENTS, componentsToProcess, security);
+ }
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10018, null, null, false);
+ // //Activator.log.debug("changeComponent method end", null);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * QueuedJob represents the items placed on the asynch dispatch queue.
+ */
+ public class QueuedJob {
+ final WorkPerformer performer;
+ /** the required type of action to do */
+ final int actionType;
+ /** work input data to be performed */
+ final Object workToDo;
+
+ /**
+ * Constructor for work queue item
+ *
+ * @param d
+ * Dispatcher for this item
+ * @param a
+ * Action for this item
+ * @param o
+ * Object for this item
+ */
+ QueuedJob(WorkPerformer d, int a, Object o) {
+ performer = d;
+ actionType = a;
+ workToDo = o;
+ }
+
+ void dispatch() {
+ try {
+ /* Call the WorkPerformer to process the work. */
+ performer.performWork(actionType, workToDo);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ log.error("[SCR] Error dispatching work ", t);
+ }
+ }
+
+ public String toString() {
+ return "[QueuedJob] WorkPerformer: " + performer + "; actionType " + actionType;
+ }
+ }
+
+ // -- begin enable/disable components
+ /**
+ * disableComponent - The specified component name must be in the same
+ * bundle as this component. Called by SC componentContext method
+ *
+ * @param name
+ * The name of a component to disable
+ * @param bundle
+ * The bundle which contains the Service Component to be disabled
+ */
+ public void disableComponent(String name, Bundle bundle) {
+ changeComponent(name, bundle, false);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.ds.util.WorkPerformer#performWork(int,
+ * java.lang.Object)
+ */
+ public void performWork(int workAction, Object workObject) {
+ if (workAction == ENABLE_COMPONENTS) {
+ resolver.enableComponents((Vector) workObject);
+ } else if (workAction == DISABLE_COMPONENTS) {
+ resolver.disableComponents((Vector) workObject);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRUtil.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRUtil.java
new file mode 100644
index 0000000..0bbbfa2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/SCRUtil.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import java.lang.reflect.Method;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.eclipse.equinox.internal.util.pool.ObjectCreator;
+import org.eclipse.equinox.internal.util.pool.ObjectPool;
+
+/**
+ * Util.java
+ *
+ * @author Valentin Valchev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public final class SCRUtil implements ObjectCreator {
+
+ private static ObjectPool objectArrayPool;
+
+ static {
+ SCRUtil u = new SCRUtil();
+ // FIXME: use some kind of logging for the object pool to determine
+ // the optimal solution!
+ objectArrayPool = new ObjectPool(u, 10, 2);
+ }
+
+ private SCRUtil() {
+ }
+
+ public static Object[] getObjectArray() {
+ return (Object[]) objectArrayPool.getObject();
+ }
+
+ public static void release(Object[] objectArray) {
+ for (int j = 0; j < objectArray.length; j++) {
+ objectArray[j] = null;
+ }
+ objectArrayPool.releaseObject(objectArray);
+ }
+
+ public Object getInstance() throws Exception {
+ return new Object[1];
+ }
+
+ public static void copyTo(Dictionary dst, Dictionary src) {
+ if (src == null || dst == null) {
+ return;
+ }
+ Object key;
+ for (Enumeration e = src.keys(); e.hasMoreElements();) {
+ key = e.nextElement();
+ dst.put(key, src.get(key));
+ }
+ }
+
+ private static Method setAccessibleMethod = null;
+ private static Object[] args = null;
+ private static boolean failed = false;
+
+ /**
+ * This method is added only for JVM compatibility. Actually setAccessible()
+ * is available since jdk1.2. The older java runtimes don't have this
+ * method.
+ *
+ * However, you can call this method. It is guaranteed that it will do the
+ * right job.
+ *
+ * @param method
+ * the method to set accessible.
+ */
+ public static final void setAccessible(Method method) {
+ try {
+ if (setAccessibleMethod == null && !failed) {
+ setAccessibleMethod = Class.forName("java.lang.reflect.AccessibleObject").getMethod("setAccessible", new Class[] {boolean.class});
+ args = new Object[] {Boolean.TRUE};
+ }
+ if (setAccessibleMethod != null)
+ setAccessibleMethod.invoke(method, args);
+ } catch (Exception e) {
+ failed = true;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ServiceReg.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ServiceReg.java
new file mode 100644
index 0000000..024418d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/ServiceReg.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import org.eclipse.equinox.internal.ds.impl.ComponentInstanceImpl;
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+import org.osgi.framework.*;
+import org.osgi.service.component.ComponentException;
+
+/**
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.2
+ */
+
+final class ServiceReg implements ServiceFactory {
+
+ static boolean dontDisposeInstances = true;
+
+ // tracking the instance usage and re-instantiation
+ private int useCount = 0;
+ private ComponentInstanceImpl instance;
+
+ // model
+ private ServiceComponentProp scp;
+
+ static {
+ String tmp = Activator.bc.getProperty("equinox.scr.dontDisposeInstances");
+ dontDisposeInstances = (tmp != null) ? !tmp.equalsIgnoreCase("false") : true;
+ }
+
+ ServiceReg(ServiceComponentProp scp, ComponentInstanceImpl instance) {
+ this.scp = scp;
+ this.instance = instance;
+ }
+
+ // ServiceFactory.getService method.
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+ try {
+ if (instance == null) {
+ instance = InstanceProcess.staticRef.buildComponent(bundle, scp, null, false);
+ //instance could be null if the component is already disposed
+ if (instance == null) {
+ return null;
+ }
+ }
+ synchronized (this) {
+ useCount++;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug("ServiceReg.getService(): service '" + scp.name + " is used " + useCount + " time(s), object = " + instance.getInstance(), null);
+ }
+ return instance.getInstance();
+ } catch (Exception e) {
+ if (!(e instanceof ComponentException)) {
+ Activator.log.error("ServiceReg.getService(): Could not create instance of " + scp.name, e);
+ return null;
+ }
+ throw (ComponentException) e;
+ }
+ }
+
+ // ServiceFactory.ungetService method.
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ boolean shallDispose = false;
+ synchronized (this) {
+ useCount--;
+ if (useCount == 0) {
+ shallDispose = true;
+ }
+ }
+ if (shallDispose) {
+ if (!dontDisposeInstances && !scp.serviceComponent.immediate) {
+ //dispose instance only if disposing is allowed and the component is not immediate one.
+
+ //Immediate components are custom case - according to me, their instances should not be disposed
+ // because they are probably needed during the whole component's life
+ if (Activator.DEBUG) {
+ Activator.log.debug("ServiceReg.ungetService(): service '" + scp.name + "' no longer used, disposing object = " + service, null);
+ }
+ // dispose only the instance - don't dispose the component
+ // itself!
+ scp.disposeObj(service);
+ // delete the instance so it can be garbage collected!
+ instance = null;
+ } else {
+ if (Activator.DEBUG) {
+ Activator.log.debug("ServiceReg.ungetService(): service '" + scp.name + " is used " + useCount + " times(s)", null);
+ }
+ }
+ } else {
+ if (useCount < 0) {
+ Activator.log.warning("ServiceReg.ungetService(): service '" + scp.name + " is used " + useCount + " times(s)", new Exception("Debug callstack"));
+ } else if (Activator.DEBUG) {
+ Activator.log.debug("ServiceReg.ungetService(): service '" + scp.name + " is used " + useCount + " times(s)", null);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return scp.name + " Service Registration";
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/TracerMap.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/TracerMap.java
new file mode 100644
index 0000000..129bf20
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/TracerMap.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import org.eclipse.equinox.internal.util.hash.HashIntObjNS;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class TracerMap {
+
+ public static HashIntObjNS getMap() {
+ HashIntObjNS map = new HashIntObjNS(61);
+
+ map.put(0, "SCR");
+ map.put(-0x0100, "SCR Activator");
+
+ /* time measurements */
+ map.put(100, "[BEGIN - start method] Creating Log instance and initializing log system took ");
+ map.put(101, "Getting FrameworkAccess service took ");
+ map.put(102, "ConfigurationAdmin ServiceTracker instantiation took ");
+ map.put(103, "ServiceTracker starting took ");
+ map.put(104, "SCRManager instantiation took ");
+ map.put(105, "addBundleListener() method took ");
+ map.put(106, "ConfigurationListener service registered for ");
+ map.put(107, "addServiceListener() method took ");
+ map.put(108, "[END - start method] Activator.start() method executed for ");
+ map.put(109, "SCR database got for ");
+ map.put(110, "Queue instantiated for ");
+ map.put(111, "Threadpool service tracker opened for ");
+ map.put(112, "Resolver instantiated for ");
+ map.put(113, "startIt() method took ");
+ map.put(114, "[BEGIN - lazy SCR init] ");
+ map.put(115, "[END - lazy SCR init] Activator.initSCR() method executed for ");
+ map.put(116, "DBManager service tracker opened for ");
+
+ // //old debug!
+ map.put(10001, "FactoryReg.getService(): created new service for component '");
+ map.put(10002, "FactoryReg.ungetService(): registration = ");
+ map.put(10003, "InstanceProcess.buildComponents(): building immediate component ");
+ map.put(10004, "InstanceProcess.buildComponents(): building component factory ");
+ map.put(10005, "InstanceProcess.buildComponent(): building component ");
+ map.put(10006, "InstanceProcess.disposeInstances(): disposing non-provider component ");
+ map.put(10007, "InstanceProcess.disposeInstances(): disposing component factory ");
+ map.put(10008, "InstanceProcess.disposeInstances(): unregistering component ");
+ map.put(10009, "InstanceProcess.disposeInstances(): cannot find registrations for ");
+ map.put(10010, "InstanceProcess.getService(): cannot get service because of circularity! Reference is: ");
+ map.put(10012, "InstanceProcess.dynamicBind(): null instances! for component ");
+ map.put(10013, "hasNext");
+ map.put(10014, "component name ");
+ map.put(10015, "SCRManager.configurationEvent(): found component - ");
+ map.put(10016, "SCRManager.stoppingBundle : ");
+ map.put(10017, "ComponentStorage.parseXMLDeclaration(): loading ");
+ map.put(10018, "changeComponent method end");
+ map.put(10019, "Resolver.enableComponents(): ignoring not enabled component ");
+ map.put(10020, "Resolver.getEligible(): processing service event ");
+ map.put(10021, "Resolver:resolveEligible(): resolved components = ");
+ map.put(10022, "Resolver.disableComponents()");
+ map.put(10023, "Resolver.performWork(): ");
+ map.put(10025, "Resolver.selectDynamicBind(): selected = ");
+ map.put(10026, "Resolver.selectDynamicUnBind(): entered");
+ map.put(10027, "Resolver.selectDynamicUnBind(): unbinding ");
+ map.put(10028, "Resolver.selectDynamicUnBind(): unbindTable is ");
+ map.put(10029, "WorkThread.Run()");
+ map.put(10030, "WorkThread.getObject ");
+ map.put(10032, "ComponentFactoryImpl.newInstance(): ");
+ map.put(10034, "getMethod() ");
+ map.put(10035, "ServiceComponentProp.dispose(): ");
+ map.put(10036, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.activate(): name: ");
+ map.put(10037, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.instance: ");
+ map.put(10038, "ServiceComponentProp.deactivate(): ");
+ map.put(10039, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.using bundle: ");
+ map.put(10040, "ServiceComponentProp:bind(): the folowing reference doesn't specify bind method : ");
+ map.put(10041, "ServiceComponentProp.bindReferences(): component ");
+ map.put(10042, "ServiceComponentProp.unbindReference(): component ");
+ map.put(10050, "Service event type: ");
+ map.put(10060, "Resolver.selectStaticUnBind(): selected = ");
+ map.put(10061, "Resolver.selectStaticBind(): selected = ");
+ map.put(10062, "Resolver.enableComponents(): ");
+ map.put(10063, "Resolver.map(): Creating SCP for component");
+ map.put(10070, "ComponentContextImpl.locateService(): ");
+ map.put(10071, "ComponentContextImpl.locateServices():");
+ map.put(10072, "ComponentContextImpl.locateServices() the specified service reference is not bound to the specified reference");
+ map.put(10080, "ComponentInstanceImpl.dispose(): disposing instance of component ");
+
+ return map;
+ }
+
+ public static HashIntObjNS getStarts() {
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkPerformer.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkPerformer.java
new file mode 100644
index 0000000..c4197c1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkPerformer.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+/**
+ * The WorkPerformer interface contains a method that is called by a queue to
+ * perform certain work.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface WorkPerformer {
+
+ /**
+ * This method can then complete processing work on the work queue thread.
+ *
+ * <p>
+ * The job queue will ignore any Throwable thrown by this method in order to
+ * continue proper dispatching the next work items.
+ *
+ * @param actionId
+ * Action ID of the work type which has to be done.
+ * @param dataToProcess
+ * The data which has to be processed
+ */
+ public void performWork(int actionId, Object dataToProcess);
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkThread.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkThread.java
new file mode 100644
index 0000000..a449614
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/WorkThread.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds;
+
+import org.osgi.service.cm.ConfigurationEvent;
+
+import org.eclipse.equinox.internal.util.event.Queue;
+import org.eclipse.equinox.internal.util.ref.TimerRef;
+import org.eclipse.equinox.internal.util.timer.TimerListener;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class WorkThread implements Runnable, TimerListener {
+
+ boolean processBundle = false;
+
+ public static int IDLE_TIMEOUT = 1000;
+ public static int BLOCK_TIMEOUT = 30000;
+ private SCRManager mgr;
+ private Object objectToProcess;
+ boolean running = true;
+
+ int waiting = 0;
+
+ public WorkThread(SCRManager mgr) {
+ this.mgr = mgr;
+ }
+
+ /**
+ * While the event queue has elements - they are processed, i.e.
+ * ManagedService(Factories) are informed for the event.
+ */
+ public void run() {
+ do {
+ try {
+ Queue queue = mgr.queue;
+ synchronized (queue) {
+ if (mgr.stopped) {
+ mgr.running = false;
+ break;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10029, null, null, false);
+ // //Activator.log.debug("WorkThread.Run()", null);
+ }
+ if (queue.size() == 0) { // wait for more events
+ try {
+ waiting++;
+ queue.wait(IDLE_TIMEOUT);
+ } catch (Exception ignore) {
+ }
+ waiting--;
+ if (mgr.stopped || queue.size() == 0) {
+ mgr.running = false;
+ break;
+ }
+ }
+ objectToProcess = queue.get();
+
+ if (objectToProcess != null) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10030, objectToProcess.toString(), null, false);
+ // //Activator.log.debug("WorkThread.getObject " +
+ // object, null);
+ }
+ } else {
+ continue;
+ }
+ }
+ TimerRef.notifyAfter(this, BLOCK_TIMEOUT, 1);
+ if (objectToProcess instanceof SCRManager.QueuedJob) {
+ ((SCRManager.QueuedJob) objectToProcess).dispatch();
+ } else if (objectToProcess instanceof ConfigurationEvent) {
+ mgr.processConfigurationEvent((ConfigurationEvent) objectToProcess);
+ }
+ } catch (Throwable t) {
+ // just for any case. Must not happen in order to keep thread
+ // alive
+ t.printStackTrace();
+ } finally {
+ TimerRef.removeListener(this, 1);
+ }
+ } while (running);
+ objectToProcess = null;
+ }
+
+ public void timer(int event) {
+ Activator.log.warning("[SCR - WorkThread] Timeout ocurred! Thread was blocked on processing " + objectToProcess, null);
+ running = false;
+ objectToProcess = null;
+ mgr.queueBlocked();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentContextImpl.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentContextImpl.java
new file mode 100644
index 0000000..5eaab6e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentContextImpl.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.impl;
+
+import java.util.Dictionary;
+import java.util.Vector;
+
+import org.eclipse.equinox.internal.ds.*;
+import org.eclipse.equinox.internal.ds.model.*;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentException;
+import org.osgi.service.component.ComponentInstance;
+
+/**
+ * ComponentContextImpl.java
+ *
+ * @author Valentin Valchev, Nina Ruseva
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ComponentContextImpl implements ComponentContext {
+
+ /* ComponentInstance instance */
+ private ComponentInstanceImpl componentInstance;
+ /* ComponentDescription */
+ private ServiceComponentProp scp;
+
+ private Bundle usingBundle;
+
+ private SCRManager mgr;
+
+ public ComponentContextImpl(ServiceComponentProp scp, Bundle usingBundle, ComponentInstanceImpl ci, SCRManager mgr) {
+ this.scp = scp;
+ this.componentInstance = ci;
+ this.usingBundle = usingBundle;
+ this.mgr = mgr;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#getProperties()
+ */
+ public Dictionary getProperties() {
+ return (Dictionary) scp.getProperties().clone();
+ }
+
+ /*
+ * (non-Javadoc) Returns the service object for the specified service
+ * reference name.
+ *
+ * @param name The name of a service reference as specified in a <code>reference</code>
+ * element in this component's description. @return A service object for the
+ * referenced service or <code>null</code> if the reference cardinality is
+ * <code>0..1</code> or <code>0..n</code> and no matching service is
+ * available. @throws ComponentException If the Service Component Runtime
+ * catches an exception while activating the target service.
+ *
+ * @see org.osgi.service.component.ComponentContext#locateService(java.lang.String)
+ */
+ public Object locateService(String name) {
+ if (scp.references == null) {
+ return null;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10070, name, null, false);
+ // //Activator.log.debug("ComponentContextImpl.locateService(): " +
+ // name, null);
+ }
+ Vector references = scp.references;
+ for (int i = 0; i < references.size(); i++) {
+ Reference reference = (Reference) references.elementAt(i);
+ ComponentReference ref = reference.reference;
+
+ // find the Reference Description with the specified name
+ if (ref.name.equals(name)) {
+ ServiceReference serviceReference = null;
+ synchronized (ref.serviceReferences) {
+ if (!ref.serviceReferences.isEmpty()) {
+ serviceReference = (ServiceReference) ref.serviceReferences.firstElement();
+ }
+ }
+ try {
+ if (serviceReference == null) {
+ // try to find service in the FW
+ ServiceReference[] serviceReferences = scp.bc.getServiceReferences(ref.interfaceName, ref.target);
+ if (serviceReferences != null && serviceReferences.length > 0) {
+ // the servicese references are sorted by
+ // service.reference and service.id
+ // so get the first one in the list
+ serviceReference = serviceReferences[0];
+ }
+
+ }
+ if (serviceReference != null) {
+ Object cached = componentInstance.bindedServices.get(serviceReference);
+ if (cached != null) {
+ // will skip the circularity checking in
+ // InstanceProcess.getService
+ return cached;
+ }
+ Object theService = InstanceProcess.staticRef.getService(reference, serviceReference);
+ // the service object could be null because of
+ // circularity
+ if (theService != null) {
+ componentInstance.bindedServices.put(serviceReference, theService);
+ return theService;
+ }
+ }
+ } catch (Throwable t) {
+ if (t instanceof ComponentException) {
+ throw (ComponentException) t;
+ }
+ throw new ComponentException("Exception occured while locating service for interface " + name, t);
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug("ComponentContextImpl.locateService(): error, service not found - " + ref.interfaceName + "; the comp. context belongs to " + scp.name, null);
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Returns the service objects for the specified service reference name.
+ *
+ * @param name The name of a service reference as specified in a <code>reference</code>
+ * element in this component's description. @return An array of service
+ * objects for the referenced service or <code>null</code> if the
+ * reference cardinality is <code>0..1</code> or <code>0..n</code> and
+ * no matching service is available.
+ *
+ * @throws ComponentException If the Service Component Runtime catches an
+ * exception while activating a target service.
+ *
+ * @see org.osgi.service.component.ComponentContext#locateServices(java.lang.String)
+ */
+ public Object[] locateServices(String name) {
+ if (scp.references == null) {
+ return null;
+ }
+
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10071, name, null, false);
+ // //Activator.log.debug("ComponentContextImpl.locateServices(): " +
+ // name, null);
+ }
+ Vector references = scp.references;
+ for (int i = 0; i < references.size(); i++) {
+ Reference reference = (Reference) references.elementAt(i);
+ ComponentReference ref = reference.reference;
+
+ if (ref.name.equals(name)) {
+ ServiceReference[] serviceReferences = null;
+ try {
+ serviceReferences = scp.bc.getServiceReferences(ref.interfaceName, ref.target);
+ if (serviceReferences != null) {
+ Vector theServices = new Vector(5);
+ Object service;
+ for (int j = 0; j < serviceReferences.length; j++) {
+ // check whether the service is cached - this will
+ // skip
+ // the circularity checking in
+ // InstanceProcess.getService
+ service = componentInstance.bindedServices.get(serviceReferences[j]);
+ if (service == null) {
+ service = InstanceProcess.staticRef.getService(reference, serviceReferences[j]);
+ }
+ // the service object could be null because of
+ // circularity
+ if (service != null) {
+ theServices.addElement(service);
+ componentInstance.bindedServices.put(serviceReferences[j], service);
+ }
+ }
+ if (!theServices.isEmpty()) {
+ Object ret[] = new Object[theServices.size()];
+ for (int j = 0; j < ret.length; j++) {
+ ret[j] = theServices.elementAt(j);
+ }
+ return ret;
+ }
+ }
+ } catch (Throwable t) {
+ if (t instanceof ComponentException) {
+ throw (ComponentException) t;
+ }
+ throw new ComponentException("Exception occured while locating services for interface " + name, t);
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#locateService(String
+ * name, ServiceReference reference)
+ */
+ public Object locateService(String name, ServiceReference serviceReference) {
+ if (scp.references == null) {
+ return null;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10070, name + " by service reference : " + serviceReference, null, false);
+ // //Activator.log.debug("ComponentContextImpl.locateService(): " +
+ // name, null);
+ }
+ Vector references = scp.references;
+ try {
+ for (int i = 0; i < references.size(); i++) {
+ Reference reference = (Reference) references.elementAt(i);
+ ComponentReference ref = reference.reference;
+
+ if (ref.name.equals(name)) {
+ if (serviceReference == null || !ref.serviceReferences.contains(serviceReference)) {
+ // the serviceReference is not bound to the specified
+ // reference
+ if (Activator.DEBUG) {
+ String referenceToString = (serviceReference == null) ? null : serviceReference.toString();
+ Activator.log.debug(0, 10072, referenceToString, null, false);
+ }
+ return null;
+ }
+
+ Object cached = componentInstance.bindedServices.get(serviceReference);
+ if (cached != null) {
+ // will skip the circularity checking in
+ // InstanceProcess.getService
+ return cached;
+ }
+ Object theService = InstanceProcess.staticRef.getService(reference, serviceReference);
+ // the service object could be null because of circularity
+ if (theService != null) {
+ componentInstance.bindedServices.put(serviceReference, theService);
+ return theService;
+ }
+ }
+ }
+ } catch (Throwable t) {
+ if (t instanceof ComponentException) {
+ throw (ComponentException) t;
+ }
+ throw new ComponentException("Exception occured while locating service for interface " + name, t);
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#getBundleContext()
+ */
+ public BundleContext getBundleContext() {
+ return scp.bc;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#getUsingBundle()
+ */
+ public Bundle getUsingBundle() {
+ // this is only for service factories!
+ ServiceComponent componentDescription = scp.serviceComponent;
+ if ((componentDescription.provides == null) || (!componentDescription.serviceFactory)) {
+ return null;
+ }
+ return usingBundle;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#getComponentInstance()
+ */
+ public ComponentInstance getComponentInstance() {
+ return componentInstance;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#enableComponent(java.lang.String)
+ */
+ public void enableComponent(String name) {
+ mgr.enableComponent(name, scp.bc.getBundle());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#disableComponent(java.lang.String)
+ */
+ public void disableComponent(String name) {
+ mgr.disableComponent(name, scp.bc.getBundle());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentContext#getServiceReference()
+ */
+ public ServiceReference getServiceReference() {
+ if (scp.serviceComponent.provides != null) {
+ ServiceRegistration reg = scp.registration;
+ if (reg != null) {
+ return reg.getReference();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentFactoryImpl.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentFactoryImpl.java
new file mode 100644
index 0000000..c6bb9a9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentFactoryImpl.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.impl;
+
+import java.util.*;
+import org.eclipse.equinox.internal.ds.*;
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+import org.eclipse.equinox.internal.util.ref.Log;
+import org.osgi.service.component.*;
+
+/**
+ * ComponentFactoryImpl.java
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ComponentFactoryImpl implements ComponentFactory {
+
+ static boolean security = false;
+
+ private ServiceComponentProp sci;
+
+ /**
+ * ComponentFactoryImpl
+ *
+ * @param component
+ * the ComponentDescription Object with Properties
+ */
+ public ComponentFactoryImpl(ServiceComponentProp component) {
+ security = Log.security();
+ this.sci = component;
+ }
+
+ /**
+ * Create a new instance of the component. Additional properties may be
+ * provided for the component instance.
+ *
+ * @param properties
+ * Additional properties for the component instance.
+ * @return A ComponentInstance object encapsulating the component instance.
+ * The returned component instance has been activated.
+ */
+ public ComponentInstance newInstance(Dictionary additionalProps) {
+ ComponentInstanceImpl instance = null;
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10032, sci.name, null, false);
+ // //Activator.log.debug("ComponentFactoryImpl.newInstance(): "
+ // + sci.name, null);
+ }
+
+ // merge properties
+ Hashtable props = (Hashtable) sci.getProperties().clone();
+ SCRUtil.copyTo(props, additionalProps);
+
+ // create a new SCP (adds to resolver scpEnabled list)
+ ServiceComponentProp newSCP = InstanceProcess.resolver.mapNewFactoryComponent(sci.serviceComponent, props);
+
+ // register the component and make instance if immediate
+ Vector toBuild = new Vector(1);
+ toBuild.addElement(newSCP);
+ InstanceProcess.staticRef.buildComponents(toBuild, security);
+ if (!newSCP.instances.isEmpty()) {
+ // an instance was built because the component is either
+ // immediate
+ // or someone has got it as service (if provides one)
+ instance = (ComponentInstanceImpl) newSCP.instances.firstElement();
+ }
+ if (instance == null) {
+ // finally build an instance if not done yet
+ instance = InstanceProcess.staticRef.buildComponent(null, newSCP, null, security);
+ }
+ instance.factory = this;
+ } catch (Throwable e) {
+ if (e instanceof ComponentException) {
+ throw (ComponentException) e;
+ }
+ Activator.log.error("ComponentFactoryImpl.newInstance(): failed for " + sci.name + " with properties " + additionalProps, e);
+ throw new ComponentException("Failed to create new instance", e);
+ }
+ return instance;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return "ComponentFactory for " + sci.name;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentInstanceImpl.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentInstanceImpl.java
new file mode 100644
index 0000000..47386ec
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/impl/ComponentInstanceImpl.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.impl;
+
+import java.util.*;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.ds.InstanceProcess;
+import org.eclipse.equinox.internal.ds.model.ServiceComponentProp;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentInstance;
+
+/**
+ * ComponentInstanceImpl.java
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ComponentInstanceImpl implements ComponentInstance {
+
+ public Object instance;
+ ServiceComponentProp scp;
+ ComponentFactoryImpl factory;
+
+ private ComponentContext componentContext;
+
+ // ServiceReference to service objects which are binded to this instance
+ public Hashtable bindedServices = new Hashtable(11);
+
+ public ComponentInstanceImpl(Object instance, ServiceComponentProp scp) {
+ this.instance = instance;
+ this.scp = scp;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentInstance#dispose()
+ */
+ public void dispose() {
+ if (scp == null) {
+ // already disposed!
+ return;
+ }
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10080, scp.name, null, false);
+ // //Activator.log.debug("ComponentInstanceImpl.dispose(): disposing
+ // instance of component " + scp.name, null);
+ }
+ if (!scp.isComponentFactory() && scp.serviceComponent.factory != null) {
+ // this is a component factory instance, so dispose SCP
+ scp.serviceComponent.componentProps.removeElement(scp);
+ Vector toDispose = new Vector(1);
+ toDispose.addElement(scp);
+ InstanceProcess.resolver.disposeComponentConfigs(toDispose);
+ scp = null;
+ } else {
+ scp.dispose(this);
+ }
+
+ // free service references if some are left ungotten
+ freeServiceReferences();
+ componentContext = null;
+ }
+
+ // check whether some cached service references are not yet removed and
+ // ungotten
+ public void freeServiceReferences() {
+ if (!bindedServices.isEmpty()) {
+ Enumeration keys = bindedServices.keys();
+ while (keys.hasMoreElements()) {
+ ServiceReference reference = (ServiceReference) keys.nextElement();
+ bindedServices.remove(reference);
+ scp.bc.ungetService(reference);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.service.component.ComponentInstance#getInstance()
+ */
+ public Object getInstance() {
+ return instance;
+ }
+
+ public ComponentContext getComponentContext() {
+ return componentContext;
+ }
+
+ public void setComponentContext(ComponentContext componentContext) {
+ this.componentContext = componentContext;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ComponentReference.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ComponentReference.java
new file mode 100644
index 0000000..ce754e7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ComponentReference.java
@@ -0,0 +1,442 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ * Jeremy Volkman - bug.id = 182560
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.model;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.eclipse.equinox.internal.ds.*;
+import org.eclipse.equinox.internal.ds.impl.ComponentInstanceImpl;
+import org.eclipse.equinox.internal.ds.model.ServiceComponent;
+import org.eclipse.equinox.internal.util.io.Externalizable;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentInstance;
+
+
+/**
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.1
+ */
+
+public class ComponentReference implements Externalizable {
+
+ public static final int CARDINALITY_0_1 = 0;
+ public static final int CARDINALITY_0_N = 1;
+ public static final int CARDINALITY_1_1 = 2;
+ public static final int CARDINALITY_1_N = 3;
+ public static final int POLICY_STATIC = 0;
+ public static final int POLICY_DYNAMIC = 1;
+
+ // --- begin: XML def
+ public String name; // required
+ public String interfaceName; // required
+ public int cardinality = CARDINALITY_1_1;
+ public int policy = POLICY_STATIC;
+ public String target;
+ public String bind;
+ public String unbind;
+ ServiceComponent component;
+ // --- end: XML def
+
+ // --- begin: cache
+ private boolean bindCached;
+ private boolean unbindCached;
+ private Method bindMethod;
+ private Method unbindMethod;
+ // --- end: cache
+
+ // --- begin: model
+
+ // ServiceReferences binded to this reference
+ public Vector serviceReferences = new Vector(2);
+
+ private Hashtable serviceReferencesToUnbind = new Hashtable(3);
+
+ static final Class[] SERVICE_REFERENCE = new Class[] {ServiceReference.class};
+
+ // --- end: model;
+
+ ComponentReference(ServiceComponent component) {
+ this.component = component;
+ if (this.component.references == null) {
+ this.component.references = new Vector(2);
+ }
+ this.component.references.addElement(this);
+ }
+
+ Method getMethod(ComponentInstanceImpl componentInstance, Reference reference, String methodName, ServiceReference serviceReference) {
+
+ Class consumerClass = componentInstance.getInstance().getClass();
+ Object serviceObject = null;
+ Class serviceObjectClass = null;
+ Class interfaceClass = null;
+ Class[] param_interfaceClass = null;
+ Method method = null;
+ while (consumerClass != null) {
+
+ // search this class' methods
+ // look for various forms of bind methods
+
+ // 1) check for bind(ServiceReference) method
+ try {
+ method = consumerClass.getDeclaredMethod(methodName, SERVICE_REFERENCE);
+ } catch (NoSuchMethodException e) {
+ } catch (NoClassDefFoundError err) {
+ // this may happen on skelmir VM
+ }
+
+ if (method != null)
+ break;
+
+ // we need a serviceObject to keep looking, create one if necessary
+ if (serviceObject == null) {
+ serviceObject = componentInstance.bindedServices.get(serviceReference);
+ if (serviceObject == null) {
+ serviceObject = InstanceProcess.staticRef.getService(reference, serviceReference);
+ }
+ if (serviceObject == null) {
+ // we could not create a serviceObject because of
+ // circularity
+ return null;
+ }
+ componentInstance.bindedServices.put(serviceReference, serviceObject);
+ serviceObjectClass = serviceObject.getClass();
+
+ // figure out the interface class - this is guaranteed to
+ // succeed or else
+ // the framework would not have let us have the service object
+ Class searchForInterfaceClass = serviceObjectClass;
+ while (searchForInterfaceClass != null) {
+ // first look through interfaces
+ Class[] interfaceClasses = searchForInterfaceClass.getInterfaces();
+ for (int i = 0; i < interfaceClasses.length; i++) {
+ if (interfaceClasses[i].getName().equals(interfaceName)) {
+ interfaceClass = interfaceClasses[i];
+ break;
+ }
+ }
+ if (interfaceClass != null) {
+ break;
+ }
+
+ // also check the class itself
+ if (searchForInterfaceClass.getName().equals(interfaceName)) {
+ interfaceClass = searchForInterfaceClass;
+ break;
+ }
+
+ // advance up the superclasses
+ searchForInterfaceClass = searchForInterfaceClass.getSuperclass();
+ }
+
+ param_interfaceClass = new Class[] {interfaceClass};
+
+ } // end if(serviceObject == null)
+
+ // 2) check for bind(Service interface) method
+ try {
+ method = consumerClass.getDeclaredMethod(methodName, param_interfaceClass);
+ } catch (NoSuchMethodException e) {
+ } catch (NoClassDefFoundError err) {
+ // this may happen on skelmir VM
+ }
+ if (method != null)
+ break;
+
+ // 3) check for bind(class.isAssignableFrom(serviceObjectClass))
+ // method
+ Method[] methods = consumerClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Class[] params = methods[i].getParameterTypes();
+ if (params.length == 1 && methods[i].getName().equals(methodName) && params[0].isAssignableFrom(serviceObjectClass)) {
+
+ method = methods[i];
+ break;
+ }
+ }
+ if (method != null)
+ break;
+ // we couldn't find the method - try the superclass
+ consumerClass = consumerClass.getSuperclass();
+ }
+
+ if (method == null) {
+ Activator.log.error("[SCR] Method was not found: " + methodName, null);
+ return null;
+ }
+
+ // if method is not protected or public, log error message
+ int modifier = method.getModifiers();
+ if (!(Modifier.isProtected(modifier) || Modifier.isPublic(modifier))) {
+ Activator.log.error("[SCR] Method " + methodName + " is not protected or public.", null);
+ return null;
+ }
+
+ if (Modifier.isProtected(modifier)) {
+ SCRUtil.setAccessible(method);
+ }
+
+ return method;
+ }
+
+ final void bind(Reference reference, ComponentInstance instance, ServiceReference serviceReference) throws Exception {
+ if (bind != null) {
+ // DON'T rebind the same object again
+ synchronized (serviceReferences) {
+ if (serviceReferences.contains(serviceReference)) {
+ Activator.log.warning("ComponentReference.bind(): service reference already bound: " + serviceReference, null);
+ return;
+ } else {
+ serviceReferences.addElement(serviceReference);
+ }
+ }
+ // retrieve the method from cache
+ if (!bindCached) {
+ bindMethod = getMethod((ComponentInstanceImpl) instance, reference, bind, serviceReference);
+ // bindMethod can be null in case of circularity
+ if (bindMethod != null) {
+ bindCached = true;
+ }
+ }
+ // invoke the method
+ if (bindMethod != null) {
+ Object methodParam = null;
+ if (bindMethod.getParameterTypes()[0].equals(ServiceReference.class)) {
+ methodParam = serviceReference;
+ } else {
+ // bindedServices is filled by the getMethod function
+ methodParam = ((ComponentInstanceImpl) instance).bindedServices.get(serviceReference);
+ if (methodParam == null) {
+ methodParam = InstanceProcess.staticRef.getService(reference, serviceReference);
+ }
+ if (methodParam == null) {
+ // cannot get serviceObject because of circularity
+
+ //remove the service reference marked as bind
+ serviceReferences.remove(serviceReference);
+ return;
+ }
+ }
+
+ Object[] params = SCRUtil.getObjectArray();
+ params[0] = methodParam;
+ try {
+ bindMethod.invoke(instance.getInstance(), params);
+ } catch (Throwable t) {
+ Activator.log.error("[SCR] Error while trying to bind reference " + this, t);
+ // rethrow exception so resolver is eventually notified that
+ // this component is bad
+ // throw t;
+ } finally {
+ SCRUtil.release(params);
+ }
+ } else {
+ //remove the service reference marked as bind
+ serviceReferences.remove(serviceReference);
+
+ // could be also circularity break
+ Activator.log.warning("ComponentReference.bind(): bind method " + bind + " is not accessible!", null);
+ }
+ }
+ }
+
+ public final void unbind(Reference reference, ComponentInstance instance, ServiceReference serviceReference) {
+ // don't unbind an object that wasn't bound
+ int index;
+ synchronized (serviceReferences) {
+ index = serviceReferences.indexOf(serviceReference);
+ if (index >= 0) {
+ if (serviceReferencesToUnbind.containsKey(serviceReference)) {
+ //the service reference is already in process of unbinding
+ return;
+ } else {
+ serviceReferencesToUnbind.put(serviceReference, "");
+ }
+ }
+ }
+ if (index == -1) {
+ Activator.log.warning("ComponentReference.unbind(): invalid service reference " + serviceReference, null);
+ return;
+ }
+ try {
+ if (unbind != null) {
+ // retrieve the unbind method from cache
+ if (!unbindCached) {
+ unbindCached = true;
+ unbindMethod = getMethod((ComponentInstanceImpl) instance, reference, unbind, serviceReference);
+ }
+ // invoke the method
+ if (unbindMethod != null) {
+ Object methodParam = null;
+ if (unbindMethod.getParameterTypes()[0].equals(ServiceReference.class)) {
+ methodParam = serviceReference;
+ } else {
+ // bindedServices is filled by the getMethod function
+ methodParam = ((ComponentInstanceImpl) instance).bindedServices.get(serviceReference);
+ if (methodParam == null) {
+ methodParam = InstanceProcess.staticRef.getService(reference, serviceReference);
+ }
+ if (methodParam == null) {
+ // probably cannot get serviceObject because of
+ // circularity
+ return;
+ }
+ }
+
+ Object[] params = SCRUtil.getObjectArray();
+ params[0] = methodParam;
+ try {
+ unbindMethod.invoke(instance.getInstance(), params);
+ } catch (Throwable t) {
+ Activator.log.error("Exception occured while unbining reference " + this, t);
+ // Activator.fwAccess.postFrameworkEvent(FrameworkEvent.ERROR,
+ // component.bundle, t);
+ } finally {
+ SCRUtil.release(params);
+ }
+ }
+ }
+ } finally {
+ synchronized (serviceReferences) {
+ serviceReferences.removeElementAt(index);
+ serviceReferencesToUnbind.remove(serviceReference);
+ }
+ }
+ if (((ComponentInstanceImpl) instance).bindedServices.remove(serviceReference) != null) {
+ component.bc.ungetService(serviceReference);
+ }
+ }
+
+ public final void dispose() {
+ bindCached = unbindCached = false;
+ bindMethod = unbindMethod = null;
+ serviceReferences = null;
+ }
+
+ public final String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("Reference[");
+ buffer.append("name = ").append(name);
+ buffer.append(", interface = ").append(interfaceName);
+ buffer.append(", policy = ");
+ switch (policy) {
+ case POLICY_DYNAMIC :
+ buffer.append("dynamic");
+ break;
+ case POLICY_STATIC :
+ buffer.append("static");
+ }
+
+ buffer.append(", cardinality = ");
+ switch (cardinality) {
+ case CARDINALITY_0_1 :
+ buffer.append("0..1");
+ break;
+ case CARDINALITY_0_N :
+ buffer.append("0..n");
+ break;
+ case CARDINALITY_1_1 :
+ buffer.append("1..1");
+ break;
+ case CARDINALITY_1_N :
+ buffer.append("1..n");
+ }
+ buffer.append(", target = ").append(target);
+ buffer.append(", bind = ").append(bind);
+ buffer.append(", unbind = ").append(unbind);
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.io.Externalizable#writeObject(java.io.OutputStream)
+ */
+ public synchronized void writeObject(OutputStream o) throws Exception {
+ try {
+
+ DataOutputStream out;
+ if (o instanceof DataOutputStream) {
+ out = (DataOutputStream) o;
+ } else {
+ out = new DataOutputStream(o);
+ }
+ boolean flag;
+ out.writeUTF(name);
+ out.writeUTF(interfaceName);
+ out.writeInt(cardinality);
+ out.writeInt(policy);
+
+ flag = target != null;
+ out.writeBoolean(flag);
+ if (flag)
+ out.writeUTF(target);
+
+ flag = bind != null;
+ out.writeBoolean(flag);
+ if (flag)
+ out.writeUTF(bind);
+
+ flag = unbind != null;
+ out.writeBoolean(flag);
+ if (flag)
+ out.writeUTF(unbind);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.io.Externalizable#readObject(java.io.InputStream)
+ */
+ public synchronized void readObject(InputStream s) throws Exception {
+ try {
+
+ DataInputStream in;
+ if (s instanceof DataInputStream) {
+ in = (DataInputStream) s;
+ } else {
+ in = new DataInputStream(s);
+ }
+ boolean flag;
+ name = in.readUTF();
+ interfaceName = in.readUTF();
+ cardinality = in.readInt();
+ policy = in.readInt();
+ flag = in.readBoolean();
+ if (flag)
+ target = in.readUTF();
+
+ flag = in.readBoolean();
+ if (flag)
+ bind = in.readUTF();
+
+ flag = in.readBoolean();
+ if (flag)
+ unbind = in.readUTF();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/DeclarationParser.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/DeclarationParser.java
new file mode 100644
index 0000000..c67fbc5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/DeclarationParser.java
@@ -0,0 +1,698 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.util.xml.*;
+import org.osgi.framework.*;
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * ComponentParser.java
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @author Teodor Bakardzhiev
+ * @version 1.0
+ */
+
+public class DeclarationParser implements ExTagListener {
+
+ private static final String XMLNS = "http://www.osgi.org/xmlns/scr/v1.0.0";
+ private static final String ATTR_XMLNS = "xmlns";
+
+ private static final String ATTR_AUTOENABLE = "enabled";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FACTORY = "factory";
+ private static final String ATTR_IMMEDIATE = "immediate";
+
+ private static final String TAG_IMPLEMENTATION = "implementation";
+ private static final String ATTR_CLASS = "class";
+
+ private static final String TAG_PROPERTY = "property";
+ private static final String ATTR_VALUE = "value";
+ private static final String ATTR_TYPE = "type";
+
+ private static final String TAG_PROPERTIES = "properties";
+ private static final String ATTR_ENTRY = "entry";
+
+ private static final String TAG_SERVICE = "service";
+ private static final String ATTR_SERVICEFACTORY = "servicefactory";
+ private static final String TAG_PROVIDE = "provide";
+ private static final String ATTR_INTERFACE = "interface";
+
+ private static final String TAG_REFERENCE = "reference";
+ private static final String ATTR_CARDINALITY = "cardinality";
+ private static final String ATTR_POLICY = "policy";
+ private static final String ATTR_TARGET = "target";
+ private static final String ATTR_BIND = "bind";
+ private static final String ATTR_UNBIND = "unbind";
+
+ public Vector components;
+
+ private Bundle bundle;
+ private BundleContext bc;
+ private ServiceComponent currentComponent;
+ private String closeTag;
+ boolean immediateSet = false;
+ private Hashtable namespaces = null;
+ private boolean rootPassed = false;
+
+ /**
+ * This method parses an XML file read from the given stream and converts
+ * the components definitions into a java object.
+ * <p>
+ *
+ * It also performs a full XML verification of the definition.�
+ *
+ * @param in
+ * the input stream to the XML declaration file
+ * @param bundle
+ * this is used to load the 'properties' tag
+ */
+ public void parse(InputStream in, Bundle bundle, Vector components) throws Exception {
+ this.components = components;
+ this.bundle = bundle;
+ this.bc = bundle.getBundleContext();
+ rootPassed = false;
+ XMLParser.parseXML(in, this, -1);
+
+ // release temporary objects
+ this.bundle = null;
+ this.bc = null;
+ this.currentComponent = null;
+ this.closeTag = null;
+ this.namespaces = null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.ExTagListener#startTag(org.eclipse.equinox.internal.util.xml.Tag)
+ */
+ public final void startTag(Tag tag) {
+ try {
+ processNamespacesEnter(tag);
+ String tagName = tag.getName();
+ if (isCorrectComponentTag(tagName)) {
+ doCorrectComponentTag(tag, tagName);
+ }
+ } catch (Throwable e) {
+ Activator.log.error("[SCR - DeclarationParser.startTag()] Error occured while processing start tag in bundle " + bundle, e);
+ } finally {
+ if (!rootPassed) {
+ rootPassed = true;
+ }
+ }
+ }
+
+ private void doEndTag(Tag tag) throws InvalidSyntaxException {
+ String tagName = tag.getName().intern();
+ if (currentComponent != null) {
+ if (tagName == TAG_IMPLEMENTATION) {
+ doImplementation(tag);
+ } else if (tagName == TAG_PROPERTY) {
+ doProperty(tag);
+ } else if (tagName == TAG_PROPERTIES) {
+ doProperties(tag);
+ } else if (tagName == TAG_SERVICE) {
+ doService(tag);
+ } else if (tagName == TAG_REFERENCE) {
+ doReference(tag);
+ } else if (tagName == TAG_PROVIDE) {
+ // empty - this tag is processed within the service tag
+ } else if (tagName == closeTag) {
+ // the component is completed - we can now fully validate it!
+
+ if (!immediateSet && (currentComponent.factory == null)) {
+ // if unset, immediate attribute is false if service element
+ // is
+ // specified or true otherwise
+ // if component factory then immediate by default is false
+ currentComponent.setImmediate(currentComponent.serviceInterfaces == null);
+ }
+ currentComponent.validate(tag.getLine());
+ if (components == null) {
+ components = new Vector(1, 1);
+ }
+ components.addElement(currentComponent);
+ currentComponent = null;
+ closeTag = null;
+ } else {
+ IllegalArgumentException e = new IllegalArgumentException("Found illegal tag named '" + tagName + "' in component XML, at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.toString(), e);
+ throw e;
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.ExTagListener#endTag(org.eclipse.equinox.internal.util.xml.Tag)
+ */
+ public final void endTag(Tag tag) {
+ try {
+ doEndTag(tag);
+ processNamespacesLeave(tag);
+ } catch (Throwable e) {
+ currentComponent = null;
+ closeTag = null;
+ Activator.log.error("[SCR - DeclarationParser.endTag()] Error occured while processing end tag in bundle " + bundle, e);
+ }
+ }
+
+ /**
+ * This method will return convert a string to a cardinality constant
+ *
+ * @param value
+ * the input string
+ * @return the cardinality or -1 to indicate error
+ */
+ private int getCardinality(String value) {
+ if ("0..1".equals(value)) {
+ return ComponentReference.CARDINALITY_0_1;
+ } else if ("0..n".equals(value)) {
+ return ComponentReference.CARDINALITY_0_N;
+ } else if ("1..1".equals(value)) {
+ return ComponentReference.CARDINALITY_1_1;
+ } else if ("1..n".equals(value)) {
+ return ComponentReference.CARDINALITY_1_N;
+ } else {
+ return -1;
+ }
+ }
+
+ private void doReference(Tag tag) throws InvalidSyntaxException {
+ String name = tag.getAttribute(ATTR_NAME);
+ if (name == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'reference' tag must have 'name' attribute, at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ String iface = tag.getAttribute(ATTR_INTERFACE);
+ if (iface == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'reference' tag must have 'interface' attribute, at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ String cardinalityS = tag.getAttribute(ATTR_CARDINALITY);
+ int cardinality = ComponentReference.CARDINALITY_1_1; // default
+ if (cardinalityS != null) {
+ cardinality = getCardinality(cardinalityS);
+ if (cardinality < 0) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'cardinality' attribute has invalid value '" + name + "' at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ } // if null - default cardinality is already initialized in
+ // constructor
+
+ String policyS = tag.getAttribute(ATTR_POLICY);
+ int policy = ComponentReference.POLICY_STATIC; // default
+ if (policyS != null) {
+ // verify the policy attribute values
+ if (policyS.equals("static")) {
+ policy = ComponentReference.POLICY_STATIC;
+ } else if (policyS.equals("dynamic")) {
+ policy = ComponentReference.POLICY_DYNAMIC;
+ } else {
+ IllegalArgumentException e = new IllegalArgumentException("The 'policy' attribute has invalid value '" + name + "' at line" + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ } // if null - default policy is already initialized in constructor
+
+ String bind = tag.getAttribute(ATTR_BIND);
+ String unbind = tag.getAttribute(ATTR_UNBIND);
+ if ((bind != null && ((unbind == null) || bind.equals(unbind))) || (unbind != null && ((bind == null) || unbind.equals(bind)))) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'reference' tag at line " + tag.getLine() + " is invalid: you must specify both but different 'bind' and 'unbind' attributes!");
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ // the reference is autoadded in the ServiceComponent's list of
+ // references
+ // in its constructor
+ ComponentReference ref = new ComponentReference(currentComponent);
+ ref.name = name;
+ ref.interfaceName = iface;
+ ref.cardinality = cardinality;
+ ref.policy = policy;
+ ref.bind = bind;
+
+ ref.unbind = unbind;
+ ref.target = tag.getAttribute(ATTR_TARGET);
+ // validate the target filter
+ if (ref.target != null) {
+ Activator.createFilter(ref.target);
+ }
+ }
+
+ private void doImplementation(Tag tag) {
+ if (currentComponent.implementation != null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'component' tag must have exactly one 'implementation' attribute at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ String tmp = tag.getAttribute(ATTR_CLASS);
+ if (tmp == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'implementation' element must have 'class' attribute set at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ currentComponent.implementation = tmp;
+ }
+
+ private void doService(Tag tag) {
+ String tmp = tag.getAttribute(ATTR_SERVICEFACTORY);
+ if (tmp != null) {
+ currentComponent.serviceFactory = Boolean.valueOf(tmp).booleanValue();
+ }
+ int size = tag.size();
+ if (size == 0) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'service' tag must have one 'provide' tag set at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ for (int i = 0; i < size; i++) {
+ Tag p = tag.getTagAt(i);
+ String pName = p.getName().intern();
+ if (pName == TAG_PROVIDE) {
+ String iFace = p.getAttribute(ATTR_INTERFACE);
+ if (iFace == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'provide' tag must have 'interface' attribute set at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ if (currentComponent.serviceInterfaces == null) {
+ currentComponent.serviceInterfaces = new Vector(size);
+ }
+ currentComponent.serviceInterfaces.addElement(iFace);
+ } else {
+ IllegalArgumentException e = new IllegalArgumentException("Found illegal element '" + pName + "' for tag 'service' at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ }
+ }
+
+ private void doProperty(Tag tag) {
+ String name = null;
+ try {
+ name = tag.getAttribute(ATTR_NAME);
+ if (name == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'property' tag must have 'name' attribute set at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ String type = tag.getAttribute(ATTR_TYPE);
+ int mtType;
+ if (type == null || "String".equals(type)) {
+ mtType = AttributeDefinition.STRING;
+ } else if ("Boolean".equals(type)) {
+ mtType = AttributeDefinition.BOOLEAN;
+ } else if ("Integer".equals(type)) {
+ mtType = AttributeDefinition.INTEGER;
+ } else if ("Long".equals(type)) {
+ mtType = AttributeDefinition.LONG;
+ } else if ("Char".equals(type)) {
+ mtType = AttributeDefinition.CHARACTER;
+ } else if ("Double".equals(type)) {
+ mtType = AttributeDefinition.DOUBLE;
+ } else if ("Float".equals(type)) {
+ mtType = AttributeDefinition.FLOAT;
+ } else if ("Byte".equals(type)) {
+ mtType = AttributeDefinition.BYTE;
+ } else if ("Short".equals(type)) {
+ mtType = AttributeDefinition.SHORT;
+ } else {
+ IllegalArgumentException e = new IllegalArgumentException("Illegal property type '" + type + "' on line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ String value = tag.getAttribute(ATTR_VALUE);
+ Object _value;
+ if (value != null) {
+ _value = makeObject(value, mtType);
+ } else {
+ // body must be specified
+ value = tag.getContent();
+ if (value == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'property' tag must have body content if 'value' attribute is not specified!");
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ StringTokenizer tok = new StringTokenizer(value, "\n\r");
+ Vector el = new Vector(10);
+ while (tok.hasMoreTokens()) {
+ String next = tok.nextToken().trim();
+ if (next.length() > 0) {
+ el.addElement(next);
+ }
+ }
+ if (el.size() == 0) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'property' tag must have body content if 'value' attribute is not specified!");
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ String[] values = new String[el.size()];
+ el.copyInto(values);
+ _value = makeArr(values, mtType);
+ }
+ if (currentComponent.properties == null) {
+ currentComponent.properties = new Properties();
+ }
+ currentComponent.properties.put(name, _value);
+ } catch (Throwable e) {
+ Activator.log.error("[SCR - DeclarationParser.doProperty()] Error while processing property " + name, e);
+ }
+ }
+
+ private void doProperties(Tag tag) {
+ String fileEntry = tag.getAttribute(ATTR_ENTRY);
+ if (fileEntry == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'properties' tag must include 'entry' attribute, at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+
+ InputStream is = null;
+ try {
+ URL resource = bundle.getResource(fileEntry);
+ is = resource != null ? resource.openStream() : null;
+ } catch (IOException ignore) {
+ }
+
+ boolean invalid = true;
+ // FIXME: this will not work on properties defined like
+ // com.prosyst.bla.component.properties
+ // in this case the path should be converted as
+ // com/prosyst/bla/component.properties
+ if (is != null) {
+ if (currentComponent.properties == null) {
+ currentComponent.properties = new Properties();
+ }
+ try {
+ currentComponent.properties.load(is);
+ invalid = false;
+ } catch (IOException e) {
+ Activator.log.error("[SCR - DeclarationParser.doProperties()] Error while loading properties file", e);
+ }
+ }
+
+ if (invalid) {
+ IllegalArgumentException e = new IllegalArgumentException("The specified 'entry' for the 'properties' tag at line " + tag.getLine() + " doesn't contain valid reference to a property file: " + fileEntry);
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ private static String COMPONENT_TAG_NAME = "component";
+
+ private void doCorrectComponentTag(Tag tag, String tagName) {
+ closeTag = tagName.intern();
+ String tmp = tag.getAttribute(ATTR_NAME);
+ if (tmp == null) {
+ IllegalArgumentException e = new IllegalArgumentException("The 'component' tag MUST have 'name' tag set, at line " + tag.getLine());
+ Activator.log.error("[SCR] " + e.getMessage(), e);
+ return;
+ }
+ immediateSet = false;
+
+ currentComponent = new ServiceComponent();
+ // make sure that the bundle attribute is set - it is required further
+ currentComponent.bundle = bundle;
+ currentComponent.bc = bc;
+ currentComponent.name = tmp;
+ tmp = tag.getAttribute(ATTR_AUTOENABLE);
+ if (tmp != null) {
+ currentComponent.autoenable = Boolean.valueOf(tmp).booleanValue();
+ }
+ tmp = tag.getAttribute(ATTR_FACTORY);
+ if (tmp != null && tmp.length() == 0) {
+ tmp = null;
+ }
+ currentComponent.factory = tmp;
+ tmp = tag.getAttribute(ATTR_IMMEDIATE);
+ if (tmp != null && tmp.length() == 0) {
+ tmp = null;
+ }
+ if (tmp != null) {
+ currentComponent.immediate = Boolean.valueOf(tmp).booleanValue();
+ immediateSet = true;
+ }
+ }
+
+ private boolean isCorrectComponentTag(String tagName) {
+ String qualifier = getNamespaceQualifier(tagName);
+ String localTagName = (qualifier.length() == 0) ? tagName : tagName.substring(qualifier.length() + 1); // + one char for ':'
+ if (!localTagName.equals(COMPONENT_TAG_NAME)) {
+ return false;
+ }
+ String namespace = getCurrentNamespace(qualifier);
+ if (!rootPassed) { // this is the root element
+ return namespace.length() == 0 || namespace.equals(XMLNS);
+ } else { // not a root element
+ return namespace.equals(XMLNS);
+ }
+ }
+
+ /**
+ * Creates an object from a <code>String</code> value and a type, as
+ * returned by the corresponding {@link AttributeDefinition#getType()}
+ * method.
+ *
+ * @param string
+ * The <code>String</code> value representation of the object.
+ * @param syntax
+ * The object's type as defined by
+ * <code>AttributeDefinition</code>.
+ *
+ * @return an Object, which is of a type, corresponding to the given, and
+ * value - got from the string parameter. E.g. if syntax is equal to
+ * <code>AttributeDefinition.INTEGER</code> and string is "1",
+ * then the value returned should be Integer("1").
+ *
+ * @exception IllegalArgumentException
+ * if a proper object can not be created due to
+ * incompatibility of syntax and value or if the parameters
+ * are not correct (e.g. syntax is not a valid
+ * <code>AttributeDefinition</code> constant).
+ */
+ public static Object makeObject(String string, int syntax) throws IllegalArgumentException {
+ try {
+ switch (syntax) {
+ case AttributeDefinition.STRING : {
+ return string;
+ }
+ case AttributeDefinition.INTEGER : {
+ return new Integer(string);
+ }
+ case AttributeDefinition.LONG : {
+ return new Long(string);
+ }
+ case AttributeDefinition.FLOAT : {
+ return new Float(string);
+ }
+ case AttributeDefinition.DOUBLE : {
+ return new Double(string);
+ }
+ case AttributeDefinition.BYTE : {
+ return new Byte(string);
+ }
+ case AttributeDefinition.SHORT : {
+ return new Short(string);
+ }
+ case AttributeDefinition.CHARACTER : {
+ if (string.length() == 0) {
+ throw new IllegalArgumentException("Missing character ");
+ }
+ return new Character(string.charAt(0));
+ }
+ case AttributeDefinition.BOOLEAN : {
+ return Boolean.valueOf(string);
+ }
+ default : {
+ throw new IllegalArgumentException("Unsupported type: ".concat(String.valueOf(syntax)));
+ }
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("Value: " + string + " does not fit its type!");
+ }
+ }
+
+ /**
+ * Makes an array from the string array value and the syntax.
+ *
+ * @param array
+ * <code>String</code> array representation of an array, which
+ * follows the rules defined by <code>AttributeDefinition</code>.
+ * @param syntax
+ * The array's type as defined by
+ * <code>AttributeDefinition</code>.
+ *
+ * @return an arary of primitives or objects, whose component type
+ * corresponds to <code>syntax</code>, and value build from the
+ * string array passed.
+ *
+ * @exception IllegalArgumentException
+ * if any of the elements in the string array can not be
+ * converted to a proper object or primitive, or if the
+ * <code>syntax</code> is not a valid
+ * <code>AttributeDefinition</code> type constant.
+ */
+ public static Object makeArr(String[] array, int syntax) throws IllegalArgumentException {
+ switch (syntax) {
+ case AttributeDefinition.STRING : {
+ return array;
+ }
+ case AttributeDefinition.INTEGER : {
+ int[] ints = new int[array.length];
+ for (int i = 0; i < array.length; i++) {
+ ints[i] = Integer.parseInt(array[i]);
+ }
+ return ints;
+ }
+ case AttributeDefinition.LONG : {
+ long[] longs = new long[array.length];
+ for (int i = 0; i < array.length; i++) {
+ longs[i] = Long.parseLong(array[i]);
+ }
+ return longs;
+ }
+ case AttributeDefinition.FLOAT : {
+ float[] floats = new float[array.length];
+ for (int i = 0; i < array.length; i++) {
+ floats[i] = Float.valueOf(array[i]).floatValue();
+ }
+ return floats;
+ }
+ case AttributeDefinition.DOUBLE : {
+ double[] doubles = new double[array.length];
+ for (int i = 0; i < array.length; i++) {
+ doubles[i] = Double.valueOf(array[i]).doubleValue();
+ }
+ return doubles;
+ }
+ case AttributeDefinition.BYTE : {
+ byte[] bytes = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ bytes[i] = Byte.parseByte(array[i]);
+ }
+ return bytes;
+ }
+ case AttributeDefinition.SHORT : {
+ short[] shorts = new short[array.length];
+ for (int i = 0; i < array.length; i++) {
+ shorts[i] = Short.parseShort(array[i]);
+ }
+ return shorts;
+ }
+ case AttributeDefinition.CHARACTER : {
+ char[] chars = new char[array.length];
+ for (int i = 0; i < array.length; i++) {
+ chars[i] = array[i].charAt(0);
+ }
+ return chars;
+ }
+ case AttributeDefinition.BOOLEAN : {
+ boolean[] booleans = new boolean[array.length];
+ for (int i = 0; i < array.length; i++) {
+ booleans[i] = Boolean.valueOf(array[i]).booleanValue();
+ }
+ return booleans;
+ }
+ default : {
+ throw new IllegalArgumentException("Unsupported type: ".concat(String.valueOf(syntax)));
+ }
+ }
+ }
+
+ private void processNamespacesEnter(Tag tag) {
+ Enumeration en = tag.getAttributeNames();
+ while (en.hasMoreElements()) {
+ String attrName = (String) en.nextElement();
+ if (attrName.startsWith(ATTR_XMLNS)) {
+ String qualifier = attrName.substring(ATTR_XMLNS.length());
+ if ((qualifier.length() == 0) || // <- default namespace
+ ((qualifier.charAt(0) == ':') ? (qualifier = qualifier.substring(1)).length() > 0 : false)) {
+
+ if (namespaces == null) {
+ namespaces = new Hashtable();
+ }
+ Stack stack = null;
+ if ((stack = (Stack) namespaces.get(qualifier)) == null) {
+ stack = new Stack();
+ namespaces.put(qualifier, stack);
+ }
+ stack.push(tag.getAttribute(attrName));
+ }
+ }
+ }
+ }
+
+ private String getCurrentNamespace(String qualifier) {
+ if (namespaces == null || qualifier == null) {
+ return "";
+ }
+ Stack stack = (Stack) namespaces.get(qualifier);
+ if (stack == null || stack.empty()) {
+ return "";
+ }
+
+ return (String) stack.peek();
+ }
+
+ private void processNamespacesLeave(Tag tag) {
+ if (namespaces == null) {
+ return;
+ }
+ Enumeration en = tag.getAttributeNames();
+ while (en.hasMoreElements()) {
+ String attrName = (String) en.nextElement();
+ if (attrName.startsWith(ATTR_XMLNS)) {
+ String qualifier = attrName.substring(ATTR_XMLNS.length());
+ if ((qualifier.length() == 0) || // <- default namespace
+ ((qualifier.charAt(0) == ':') ? (qualifier = qualifier.substring(1)).length() > 0 : false)) {
+
+ Stack stack = (Stack) namespaces.get(qualifier);
+ if (stack != null) {
+ if (!stack.empty()) {
+ stack.pop();
+ }
+ if (stack.empty()) {
+ namespaces.remove(qualifier);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private String getNamespaceQualifier(String name) {
+ int index = name.indexOf(':');
+ if (index < 0) {
+ return "";
+ }
+ return name.substring(0, index);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponent.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponent.java
new file mode 100644
index 0000000..8f7f14c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponent.java
@@ -0,0 +1,432 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.model;
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.ds.SCRUtil;
+import org.eclipse.equinox.internal.util.io.Externalizable;
+import org.eclipse.equinox.internal.util.io.ExternalizableDictionary;
+import org.osgi.framework.*;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.ComponentException;
+
+/**
+ * This is an OO wrapper for the XML representing the components. It also caches
+ * the (de)activate & (un)bind methods (reflection) of the component.
+ *
+ * @author Valentin Valchev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ServiceComponent implements Externalizable {
+
+ public Vector componentProps = null;
+
+ // --- begin: XML def
+ public String name;
+ public String factory;
+ String implementation; // the class name
+ Properties properties; // property or properties
+
+ // service
+ public Vector serviceInterfaces; // all strings
+ public String[] provides; // the same as above, but as String[]
+ public boolean serviceFactory = false;
+
+ public Vector references; // ComponentReference
+
+ public boolean autoenable = true;
+ public boolean immediate = false;
+ // --- end: XML def
+
+ // --- begin: cache
+ private boolean activateCached = false;
+ private boolean deactivateCached = false;
+ private Method activate; // this is optional!
+ private Method deactivate;
+ // --- end: cache
+
+ // --- begin: model
+ public boolean enabled;
+ public Bundle bundle;
+ public BundleContext bc;
+ // --- end: model
+
+ private static final Class ACTIVATE_METHODS_PARAMETERS[] = new Class[] {ComponentContext.class};
+
+ public ServiceComponent() {
+ }
+
+ private static final Method getMethod(Object instance, String name) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10034, name, null, false);
+ // //Activator.log.debug("getMethod() " + name, null);
+ }
+ Method method = null;
+ Class clazz = instance != null ? instance.getClass() : null;
+
+ // search for the method in the parent classes!
+ while (method == null && clazz != null) {
+ try {
+ method = clazz.getDeclaredMethod(name, ACTIVATE_METHODS_PARAMETERS);
+ } catch (Throwable t) {
+ }
+ clazz = clazz.getSuperclass();
+ }
+ if (method != null) {
+ int modifiers = method.getModifiers();
+ if (Modifier.isProtected(modifiers)) {
+ SCRUtil.setAccessible(method);
+ } else if (!Modifier.isPublic(modifiers)) {
+ // not protected neither public
+ method = null;
+ }
+ }
+
+ return method;
+ }
+
+ void activate(Object instance, ComponentContext context) throws ComponentException {
+ // retrieve the activate method from cache
+ if (!activateCached) {
+ activateCached = true;
+ activate = getMethod(instance, "activate");
+ }
+ // invoke the method if any
+ if (activate != null) {
+ Object[] params = SCRUtil.getObjectArray();
+ params[0] = context;
+ try {
+ activate.invoke(instance, params);
+ } catch (Throwable t) {
+ Activator.log.error("[SCR] Cannot activate instance " + instance + " of component " + name, t);
+ throw new ComponentException("[SCR] Exception while activating instance " + instance + " of component " + name, t);
+ // rethrow exception so resolver is eventually notified that
+ // the processed SCP is bad
+ } finally {
+ SCRUtil.release(params);
+ }
+ }
+ }
+
+ void deactivate(Object instance, ComponentContext context) {
+ // retrieve the activate method from cache
+ if (!deactivateCached) {
+ deactivateCached = true;
+ deactivate = getMethod(instance, "deactivate");
+ }
+ // invoke the method
+ if (deactivate != null) {
+ Object[] params = SCRUtil.getObjectArray();
+ params[0] = context;
+ try {
+ deactivate.invoke(instance, params);
+ } catch (Throwable t) {
+ Activator.log.error("[SCR] Error while attempting to deactivate instance of component " + name, t);
+ } finally {
+ SCRUtil.release(params);
+ }
+ }
+ }
+
+ /**
+ * this method is called from the xml parser to validate the component once
+ * it is fully loaded!
+ *
+ * @param line
+ * the line at which the the component definition ends
+ */
+ void validate(int line) {
+ // name & implementations are required
+ if (name == null) {
+ throw new IllegalArgumentException("The component definition misses 'name' attribute, line " + line);
+ }
+ if (implementation == null) {
+ throw new IllegalArgumentException("The component '" + name + "' misses 'implementation' attribute, line " + line);
+ }
+
+ // component factory is incompatible with service factory
+ if (factory != null && serviceFactory) {
+ throw new IllegalArgumentException("The component '" + name + "' invalid specifies both ComponentFactory and ServiceFactory");
+ }
+
+ if (immediate) {
+ if (serviceFactory)
+ throw new IllegalArgumentException("The component '" + name + "' is invalid specified both as immediate and ServiceFactory");
+ if (factory != null)
+ throw new IllegalArgumentException("The component '" + name + "' is invalid specified both as immediate and ComponentFactory");
+ } else {
+ if ((serviceInterfaces == null) && (factory == null)) {
+ throw new IllegalArgumentException("The component '" + name + "' is invalid specifying immediate to false and providing no Services");
+ }
+ }
+
+ // make sure that references are also valid
+ if (references != null) {
+ for (int i = 0; i < references.size(); i++) {
+ ComponentReference r = (ComponentReference) references.elementAt(i);
+ if (r.name == null || r.interfaceName == null) {
+ throw new IllegalArgumentException("The component '" + name + "' defined at line " + line + " contains illegal reference " + r);
+ }
+ }
+ }
+
+ // cache the service interfaces as String[] too.
+ if (serviceInterfaces != null && !serviceInterfaces.isEmpty()) {
+ provides = new String[serviceInterfaces.size()];
+ serviceInterfaces.copyInto(provides);
+ }
+
+ // make sure that the component will get automaticaly enabled/disable!
+ enabled = autoenable;
+ }
+
+ /**
+ * This method will instantiate the implementation class!
+ *
+ * @return instance of the component implementation class. If the components
+ * exports some services, the implementation must implement all of
+ * them
+ * @throws Exception
+ * is thrown if the implementation cannot be instantiated for
+ * some reasons.
+ */
+ final Object createInstance() throws Exception {
+ try {
+ return bundle.loadClass(implementation).newInstance();
+ } catch (Throwable t) {
+ // Activator.log.error("[SCR] Unable to create an instance of
+ // component "+name, t);
+ throw new ComponentException("Exception occured while creating new instance of component " + name, t);
+ }
+ }
+
+ /**
+ * This method will dispose everything
+ */
+ // TODO : this method is not used - should be removed?
+ public final void dispose() {
+
+ activateCached = deactivateCached = false;
+ activate = deactivate = null;
+
+ enabled = false;
+ bundle = null;
+ // bc = null;
+
+ if (references != null) {
+ for (int i = 0; i < references.size(); i++) {
+ ComponentReference ref = (ComponentReference) references.elementAt(i);
+ ref.dispose();
+ }
+ references.removeAllElements();
+ references = null;
+ }
+
+ if (properties != null) {
+ properties.clear();
+ properties = null;
+ }
+
+ if (serviceInterfaces != null) {
+ serviceInterfaces.removeAllElements();
+ serviceInterfaces = null;
+ }
+ }
+
+ public boolean provides(String interfaceName) {
+ return serviceInterfaces != null && serviceInterfaces.contains(interfaceName);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ public final String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("Component[");
+ buffer.append("\n\tname = ").append(name);
+ buffer.append("\n\tautoenable = ").append(autoenable);
+ buffer.append("\n\tfactory = ").append(factory);
+ buffer.append("\n\timmediate = ").append(immediate);
+
+ buffer.append("\n\timplementation = ").append(implementation);
+ buffer.append("\n\tproperties = ").append(properties);
+
+ buffer.append("\n\tserviceFactory = ").append(serviceFactory);
+ buffer.append("\n\tserviceInterface = ").append(serviceInterfaces);
+
+ if (references == null) {
+ buffer.append("\n\treferences = ").append("null");
+ } else {
+ buffer.append("\n\treferences = {");
+ for (int i = 0; i < references.size(); i++) {
+ buffer.append("\n\t\t").append(references.elementAt(i));
+ }
+ buffer.append("\n\t}");
+ }
+ buffer.append("\n]");
+ return buffer.toString();
+ }
+
+ public synchronized void writeObject(OutputStream s) throws Exception {
+
+ try {
+
+ DataOutputStream out;
+ if (s instanceof DataOutputStream) {
+ out = (DataOutputStream) s;
+ } else {
+ out = new DataOutputStream(s);
+ }
+
+ boolean flag;
+ int count;
+
+ out.writeUTF(name);
+ out.writeUTF(implementation);
+ out.writeBoolean(serviceFactory);
+ out.writeBoolean(autoenable);
+ out.writeBoolean(immediate);
+
+ flag = factory != null;
+ out.writeBoolean(flag);
+ if (flag)
+ out.writeUTF(factory);
+
+ count = serviceInterfaces == null ? 0 : serviceInterfaces.size();
+ out.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ out.writeUTF(serviceInterfaces.elementAt(i).toString());
+ }
+
+ count = references == null ? 0 : references.size();
+ out.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ ComponentReference ref = (ComponentReference) references.elementAt(i);
+ ref.writeObject(out);
+ }
+
+ flag = properties != null && !properties.isEmpty();
+ out.writeBoolean(flag);
+ if (flag) {
+ ExternalizableDictionary dictionary = new ExternalizableDictionary();
+ dictionary.copyFrom(properties);
+ dictionary.writeObject(out);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Don't forget to set the bundle & the bc attributes!!!!!!
+ *
+ * @param s
+ * the input stream from which to read the object
+ * @throws Exception
+ * @see org.eclipse.equinox.internal.util.io.Externalizable#readObject(java.io.InputStream)
+ */
+ public synchronized void readObject(InputStream s) throws Exception {
+ try {
+
+ DataInputStream in;
+ if (s instanceof DataInputStream) {
+ in = (DataInputStream) s;
+ } else {
+ in = new DataInputStream(s);
+ }
+ boolean flag;
+ int count;
+
+ name = in.readUTF();
+ implementation = in.readUTF();
+ serviceFactory = in.readBoolean();
+ autoenable = in.readBoolean();
+ immediate = in.readBoolean();
+
+ flag = in.readBoolean();
+ if (flag)
+ factory = in.readUTF();
+
+ count = in.readInt();
+ if (count > 0) {
+ serviceInterfaces = new Vector(count);
+ provides = new String[count];
+ for (int i = 0; i < count; i++) {
+ String entry = in.readUTF();
+ serviceInterfaces.addElement(entry);
+ provides[i] = entry;
+ }
+ }
+
+ count = in.readInt();
+ if (count > 0) {
+ references = new Vector(count);
+ for (int i = 0; i < count; i++) {
+ ComponentReference ref = new ComponentReference(this);
+ ref.readObject(in);
+ }
+ }
+
+ flag = in.readBoolean();
+ if (flag) {
+ ExternalizableDictionary dictionary = new ExternalizableDictionary();
+ dictionary.readObject(in);
+ Properties props = new Properties();
+ for (Enumeration keys = dictionary.keys(); keys.hasMoreElements();) {
+ String key = (String) keys.nextElement();
+ props.put(key, dictionary.get(key));
+ }
+ properties = props;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public ServiceComponentProp getComponentPropByPID(String pid) {
+ for (int i = 0; i < componentProps.size(); i++) {
+ ServiceComponentProp scp = (ServiceComponentProp) componentProps.elementAt(i);
+ if (scp.getProperties() != null) {
+ if (pid.equals(scp.getProperties().get(Constants.SERVICE_PID))) {
+ return scp;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void addServiceComponentProp(ServiceComponentProp scp) {
+ if (componentProps == null) {
+ componentProps = new Vector(2);
+ }
+ if (!componentProps.contains(scp)) {
+ componentProps.addElement(scp);
+ }
+ }
+
+ public boolean isImmediate() {
+ return immediate;
+ }
+
+ public void setImmediate(boolean immediate) {
+ this.immediate = immediate;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponentProp.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponentProp.java
new file mode 100644
index 0000000..a238fe9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/model/ServiceComponentProp.java
@@ -0,0 +1,555 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.model;
+
+import java.security.*;
+import java.util.*;
+import org.eclipse.equinox.internal.ds.*;
+import org.eclipse.equinox.internal.ds.impl.ComponentContextImpl;
+import org.eclipse.equinox.internal.ds.impl.ComponentInstanceImpl;
+import org.osgi.framework.*;
+import org.osgi.service.component.*;
+
+/**
+ * The ServiceComponentProp represents a ServiceComponent mapped to it's CM
+ * properties.
+ *
+ * When the ServiceComponent contains plain CM properties, the mapping of
+ * ServiceComponentProp to ServiceComponent is 1:1.
+ *
+ * However, when the ServiceComponent is a ManagedServiceFactory, there are so
+ * many ServiceComponentProp objects created as many are the configurations
+ * associated with the component.
+ *
+ * @author Valentin Valchev
+ * @author Stoyan Boshev
+ * @author Pavlin Dobrev
+ * @version 1.2
+ */
+
+public class ServiceComponentProp implements PrivilegedExceptionAction {
+ public ServiceRegistration registration;
+ public String name;
+ public ServiceComponent serviceComponent;
+ public Hashtable properties;
+ public Vector instances = new Vector(2); // ComponentInstance objects
+ public BundleContext bc;
+ public Vector references;
+
+ private boolean kindOfFactory;
+
+ // This flag is used to check whether the component is a component factory.
+ // Since the component factory creates new ServiceComponentProp objects they
+ // have to
+ // be marked as non component factory components in order to prevent newly
+ // registered
+ // component factories as services.
+ protected boolean isComponentFactory = false;
+
+ //Used to mark the component as disposed
+ public boolean disposed = false;
+
+ /**
+ * List of names (Strings) of Component Configurations we should not
+ * activate during the activation of this Component Configuration. This is
+ * populated by the {@link org.eclipse.equinox.internal.ds.Resolver Resolver}
+ * and used by
+ * {@link org.eclipse.equinox.internal.ds.InstanceProcess InstanceProcess}.
+ */
+ protected Vector delayActivateSCPNames;
+
+ private SCRManager mgr;
+
+ // next free component id
+ private static long componentid = 0;
+
+ public ServiceComponentProp(ServiceComponent serviceComponent, Dictionary configProperties, SCRManager mgr) {
+
+ this.serviceComponent = serviceComponent;
+ this.name = serviceComponent.name;
+ this.bc = serviceComponent.bc;
+
+ initProperties(configProperties);
+
+ // cache locally if it is some kind of a factory
+ kindOfFactory = serviceComponent.factory != null || serviceComponent.serviceFactory;
+ isComponentFactory = serviceComponent.factory != null;
+
+ // used for component context
+ this.mgr = mgr;
+ }
+
+ /**
+ * This method will dispose the service component instance. Along with the
+ * service component itself and the properties files.
+ */
+ public void dispose() {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10035, name, null, false);
+ // //Activator.log.debug("ServiceComponentProp.dispose(): ", null);
+ }
+ disposed = true;
+ while (!instances.isEmpty()) {
+ ComponentInstanceImpl current = (ComponentInstanceImpl) instances.firstElement();
+ dispose(current);
+ current.dispose();
+ }
+ }
+
+ /**
+ * getProperties
+ *
+ * @return Dictionary properties
+ */
+ public Hashtable getProperties() {
+ return properties != null ? properties : serviceComponent.properties;
+ }
+
+ /**
+ * This method will call the activate method on the specified object.
+ *
+ * @param usingBundle
+ * the bundle that is using the component - this hase only means
+ * when the component is factory
+ * @param componentInstance
+ * the component instance whcich will be activated.
+ * @throws Exception
+ * could be thrown if the activate fails for some reason but NOT
+ * in case, if the instance doesn't define an activate method.
+ */
+ public void activate(Bundle usingBundle, ComponentInstanceImpl componentInstance) throws Exception {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10036, name, null, false);
+ // //Activator.log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.activate():
+ // name: " + name, null);
+ Activator.log.debug(0, 10039, (usingBundle != null ? usingBundle.getSymbolicName() : null), null, false);
+ // //Activator.log.debug(
+ // // "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.using
+ // bundle: "
+ // // + (usingBundle != null ? usingBundle.getSymbolicName() :
+ // null),
+ // // null);
+ Activator.log.debug(0, 10037, componentInstance.toString(), null, false);
+ // //Activator.log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ServiceComponentProp.instance:
+ // " + componentInstance, null);
+ }
+
+ // call the activate method on the Service Component
+ serviceComponent.activate(componentInstance.getInstance(), componentInstance.getComponentContext());
+ }
+
+ /**
+ * This method will call the deactivate method on the specified Object.
+ * Notice, that this is not the ComponentInstance object, but the real
+ * instance.
+ *
+ * @param componentInstance
+ * component instance wrapper
+ */
+ private void deactivate(ComponentInstanceImpl componentInstance) {
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10038, name, null, false);
+ // //Activator.log.debug("ServiceComponentProp.deactivate(): " +
+ // name, null);
+ }
+ serviceComponent.deactivate(componentInstance.getInstance(), componentInstance.getComponentContext());
+ }
+
+ /**
+ * Call the bind method for each of the Referenced Services in this Service
+ * Component
+ *
+ * @param componentInstance
+ * @throws Exception
+ */
+ public void bind(ComponentInstance componentInstance) throws Exception {
+ // Get all the required service Reference Descriptions for this Service
+ // Component
+ // call the Bind method if the Reference Description includes one
+ if (references != null) {
+ for (int i = 0; i < references.size(); i++) {
+ Reference ref = (Reference) references.elementAt(i);
+ if (ref.reference.bind != null) {
+ bindReference(ref, componentInstance);
+ } else if (Activator.DEBUG) {
+ Activator.log.debug(0, 10040, ref.reference.name, null, false);
+ // //Activator.log.debug(
+ // // "ServiceComponentProp:bind(): the reference '" +
+ // ref.name + "' doesn't specify bind method",
+ // // null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Call the unbind method for each of the Referenced Services in this
+ * Serivce Component
+ *
+ * @param componentInstance
+ * the object which unbind method(s) will be called, if ANY!
+ */
+ public void unbind(ComponentInstance componentInstance) {
+ // call the unBind method if the Reference Description includes one
+ if (references != null) {
+ for (int i = references.size() - 1; i >= 0; i--) {
+ Reference ref = (Reference) references.elementAt(i);
+ if (ref.reference.unbind != null) {
+ unbindReference(ref, componentInstance);
+ }
+ }
+ }
+ }
+
+ public Object createInstance() throws Exception {
+ assertCreateSingleInstance();
+ return serviceComponent.createInstance();
+ }
+
+ boolean locked = false;
+ int waiting = 0;
+
+ Bundle bundle;
+ Object inst;
+
+ synchronized void lock(Bundle usingBundle, Object instance) {
+ while (locked) {
+ try {
+ waiting++;
+ wait();
+ } catch (Exception _) {
+ }
+ waiting--;
+ }
+ locked = true;
+ bundle = usingBundle;
+ inst = instance;
+ }
+
+ synchronized void unlock() {
+ locked = false;
+ bundle = null;
+ inst = null;
+ if (waiting > 0)
+ notifyAll();
+ }
+
+ public Object run() throws Exception {
+ Bundle bundle = this.bundle;
+ Object instance = inst;
+ unlock();
+ return build(bundle, instance, false);
+ }
+
+ public ComponentInstanceImpl build(Bundle usingBundle, Object instance, boolean security) throws Exception {
+ if (disposed) {
+ if (Activator.DEBUG) {
+ Activator.log.debug("Cannot build component, because it is already disposed: " + this, null);
+ }
+ return null;
+ }
+
+ if (security) {
+ this.lock(usingBundle, instance);
+ try {
+ return (ComponentInstanceImpl) AccessController.doPrivileged(this);
+ } catch (PrivilegedActionException pae) {
+
+ }
+ }
+ ComponentInstanceImpl componentInstance = null;
+ if (instance == null) {
+ if (!kindOfFactory) {
+ // it is a plain service, this is because this method
+ // is also called from ServiceReg
+ if (instances.isEmpty()) {
+ instance = createInstance();
+ } else {
+ componentInstance = (ComponentInstanceImpl) instances.firstElement();
+ }
+ } else {
+ instance = createInstance();
+ }
+ }
+ if (componentInstance == null) {
+ componentInstance = new ComponentInstanceImpl(instance, this);
+ componentInstance.setComponentContext(new ComponentContextImpl(this, usingBundle, componentInstance, mgr));
+ instances.addElement(componentInstance);
+ bind(componentInstance);
+ try {
+ activate(usingBundle, componentInstance);
+ } catch (Exception e) {
+ //must unbind and dispose this component instance
+ InstanceProcess.resolver.removeFromSatisfiedList(this);
+ if (instances.removeElement(componentInstance)) {
+ unbind(componentInstance);
+ }
+ throw e;
+ }
+ }
+
+ return componentInstance;
+ }
+
+ public void disposeObj(Object obj) {
+ ComponentInstanceImpl ci = null;
+ synchronized (instances) {
+ for (int i = 0; i < instances.size(); i++) {
+ ci = (ComponentInstanceImpl) instances.elementAt(i);
+ if (ci.getInstance() == obj) {
+ break;
+ } else {
+ ci = null;
+ }
+ }
+ }
+ if (ci != null) {
+ dispose(ci);
+ return;
+ }
+ throw new RuntimeException("The Object '" + obj + "' is not created by the component named " + name);
+ }
+
+ public void dispose(ComponentInstanceImpl componentInstance) {
+ if (!instances.removeElement(componentInstance)) {
+ return; //the instance is already disposed
+ }
+
+ deactivate(componentInstance);
+ unbind(componentInstance);
+ }
+
+ /**
+ * Call the bind method for this referenceDescription
+ *
+ * @param componentReference
+ * @param componentInstance
+ * @throws Exception
+ *
+ */
+ public void bindReference(Reference reference, ComponentInstance componentInstance) throws Exception {
+
+ // bind the services ONLY if bind method is specified!
+ if (reference.reference.bind == null) {
+ return;
+ }
+
+ // ok, proceed!
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10041, serviceComponent.name + " -> " + reference.reference, null, false);
+ // //Activator.log.debug(
+ // // "ServiceComponentProp.bindReferences(): component " +
+ // serviceComponent.name + " to " + componentReference,
+ // // null);
+ }
+
+ ServiceReference[] serviceReferences = null;
+
+ // if there is a published service, then get the ServiceObject and call
+ // bind
+ try {
+ // get all registered services using this target filter
+ serviceReferences = bc.getServiceReferences(reference.reference.interfaceName, reference.reference.target);
+ } catch (Exception e) {
+ Activator.log.error("[SCR] Cannot get references for " + reference.reference.interfaceName, e);
+ throw e;
+ // rethrow exception so resolver is eventually notified that this
+ // SCP is bad
+ }
+
+ // bind only if there is at least ONE service
+ if (serviceReferences != null && serviceReferences.length > 0) {
+ // the component binds to the first available service only!
+ switch (reference.reference.cardinality) {
+ case ComponentReference.CARDINALITY_1_1 :
+ case ComponentReference.CARDINALITY_0_1 :
+ reference.reference.bind(reference, componentInstance, serviceReferences[0]);
+ break;
+ case ComponentReference.CARDINALITY_1_N :
+ case ComponentReference.CARDINALITY_0_N :
+ // bind to all services
+ for (int i = 0; i < serviceReferences.length; i++) {
+ reference.reference.bind(reference, componentInstance, serviceReferences[i]);
+ }
+ break;
+ }
+ } else {
+ if (Activator.DEBUG) {
+ Activator.log.debug("ServiceComponentProp.bindReference(): The service is not yet registered, but it is already instantiated", null);
+ }
+ }
+ }
+
+ /**
+ * Call the unbind method for this Reference Description
+ *
+ * @param reference
+ * @param componentInstance
+ */
+ public void unbindReference(Reference reference, ComponentInstance componentInstance) {
+
+ // unbind the services ONLY if unbind method is specified!
+ if (reference.reference.unbind == null) {
+ return;
+ }
+
+ // ok, proceed!
+
+ if (Activator.DEBUG) {
+ Activator.log.debug(0, 10042, serviceComponent.name + " <- " + reference.reference, null, false);
+ // //Activator.log.debug(
+ // // "ServiceComponentProp.unbindReference(): component " + name +
+ // " from " + componentReference,
+ // // null);
+ }
+
+ Vector serviceReferences = reference.reference.serviceReferences;
+ for (int i = serviceReferences.size() - 1; i >= 0; i--) {
+ reference.reference.unbind(reference, componentInstance, (ServiceReference) serviceReferences.elementAt(i));
+ }
+ }
+
+ /**
+ * Call the unbind method for this Reference Description
+ *
+ * @param ref
+ * @param instance
+ * @param serviceObject
+ * @throws Exception
+ */
+ public void unbindDynamicReference(Reference ref, ComponentInstance instance, ServiceReference serviceReference) throws Exception {
+ try {
+ if (Activator.DEBUG) {
+ Activator.log.debug("ServiceComponentProp.unbindDynamicReference(): component = " + name + ", reference = " + ref.reference.name, null);
+ }
+ // check if we need to rebind
+ switch (ref.reference.cardinality) {
+ case ComponentReference.CARDINALITY_0_1 :
+ case ComponentReference.CARDINALITY_1_1 :
+ if (ref.reference.bind != null) {
+ bindReference(ref, instance);
+ }
+ }
+ ref.reference.unbind(ref, instance, serviceReference);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ // -- begin helper methods
+ /**
+ * Initialize Properties for this Component
+ *
+ * The property elements provide default or supplemental property values if
+ * not overridden by the properties retrieved from Configuration Admin
+ *
+ * @param configProperties
+ * the configuration properties
+ */
+ private void initProperties(Dictionary configProperties) {
+ // default component service properties
+ Properties propertyDescriptions = serviceComponent.properties;
+ if (propertyDescriptions != null && !propertyDescriptions.isEmpty()) {
+ properties = (Hashtable) propertyDescriptions.clone();
+ }
+
+ // set the component.name
+ if (properties == null) {
+ properties = new Hashtable(7);
+ }
+
+ // put the references in the properties
+ if (serviceComponent.references != null) {
+ ComponentReference ref;
+ for (int i = 0; i < serviceComponent.references.size(); i++) {
+ ref = (ComponentReference) serviceComponent.references.elementAt(i);
+ if (ref.target != null) {
+ properties.put(ref.name + ComponentConstants.REFERENCE_TARGET_SUFFIX, ref.target);
+ }
+ }
+ }
+
+ // properties from Configuration Admin
+ if (configProperties != null && !configProperties.isEmpty()) {
+ for (Enumeration keys = configProperties.keys(); keys.hasMoreElements();) {
+ Object key = keys.nextElement();
+ Object val = configProperties.get(key);
+ properties.put(key, val);
+ }
+ }
+
+ // always set the component name & the id
+ Long nextId = new Long(getNewComponentID());
+ properties.put(ComponentConstants.COMPONENT_ID, nextId);
+ properties.put(ComponentConstants.COMPONENT_NAME, serviceComponent.name);
+
+ if (serviceComponent.provides != null) {
+ String[] provides = new String[serviceComponent.provides.length];
+ System.arraycopy(serviceComponent.provides, 0, provides, 0, provides.length);
+ properties.put(Constants.OBJECTCLASS, provides);
+ }
+ }
+
+ private void assertCreateSingleInstance() {
+ if (!kindOfFactory && !instances.isEmpty()) {
+ throw new ComponentException("Instance of '" + name + "'is already created!");
+ }
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public void setRegistration(ServiceRegistration reg) {
+ registration = reg;
+ }
+
+ /**
+ * Add a new Component Configuration name we should not activate in order to
+ * prevent a cycle.
+ *
+ * @see ServiceComponentProp#delayActivateSCPNames
+ */
+ public void setDelayActivateSCPName(String scpName) {
+ if (delayActivateSCPNames == null) {
+ delayActivateSCPNames = new Vector(1);
+ delayActivateSCPNames.addElement(scpName);
+ } else if (!delayActivateSCPNames.contains(scpName)) {
+ delayActivateSCPNames.addElement(scpName);
+ }
+ }
+
+ public Vector getDelayActivateSCPNames() {
+ return delayActivateSCPNames;
+ }
+
+ public boolean isComponentFactory() {
+ return isComponentFactory;
+ }
+
+ // used by the ComponentFactoryImpl to mark component configs created by it
+ // that
+ // they are not component factories. This avoids subsecuent registrations of
+ // the same component factory.
+ public void setComponentFactory(boolean isComponentFactory) {
+ this.isComponentFactory = isComponentFactory;
+ }
+
+ static synchronized long getNewComponentID() {
+ return componentid++;
+ }
+
+ public boolean isKindOfFactory() {
+ return kindOfFactory;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/DBObject.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/DBObject.java
new file mode 100644
index 0000000..aa8d932
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/DBObject.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.storage.file;
+
+import java.io.*;
+import java.util.Vector;
+import org.eclipse.equinox.internal.ds.model.ServiceComponent;
+
+/**
+ * @author Nina Ruseva
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class DBObject implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public DBObject() {
+ }
+
+ public Vector components;
+
+ public DBObject(Vector components) {
+ this.components = components;
+ }
+
+ public void writeObject(OutputStream out) throws Exception {
+ DataOutputStream dataOut;
+ if (out instanceof DataOutputStream) {
+ dataOut = (DataOutputStream) out;
+ } else {
+ dataOut = new DataOutputStream(out);
+ }
+ dataOut.writeInt(components == null ? 0 : components.size());
+ if (components != null && components.size() > 0) {
+ for (int k = 0; k < components.size(); k++) {
+ ((ServiceComponent) components.elementAt(k)).writeObject(dataOut);
+ }
+ }
+ }
+
+ public void readObject(InputStream in) throws Exception {
+ DataInputStream dataIn;
+ if (in instanceof DataInputStream) {
+ dataIn = (DataInputStream) in;
+ } else {
+ dataIn = new DataInputStream(in);
+ }
+
+ int size = dataIn.readInt();
+ components = new Vector();
+ for (int k = 0; k < size; k++) {
+ ServiceComponent component = new ServiceComponent();
+ component.readObject(dataIn);
+ components.addElement(component);
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/FileStorage.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/FileStorage.java
new file mode 100644
index 0000000..1fc4d68
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/ds/storage/file/FileStorage.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.ds.storage.file;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Dictionary;
+import java.util.Vector;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.ds.ComponentStorage;
+import org.eclipse.equinox.internal.ds.model.ServiceComponent;
+import org.eclipse.equinox.internal.util.io.ExternalizableDictionary;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.*;
+import org.osgi.service.component.ComponentConstants;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.1
+ */
+
+public class FileStorage extends ComponentStorage {
+
+ //TODO: this constant should be public and shared across other bundles that use the same property.
+ //Probably it should be in the supplement bundle?
+ public static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration";
+
+ private BundleContext bc;
+
+ private String[] dbBundlePath = new String[1];
+ private String[] dbCompPath = new String[] {null, "COMPONENTS"};
+ private static String CUSTOM_DB_NAME = "SCR";
+ private File file;
+ private ExternalizableDictionary data = new ExternalizableDictionary();
+ private StringBuffer pathBuffer = new StringBuffer();
+ private String separator;
+
+ public FileStorage(BundleContext bc) {
+ this.bc = bc;
+ separator = bc.getProperty("path.separator");
+ file = bc.getDataFile(CUSTOM_DB_NAME);
+ try {
+ if (file.exists()) {
+ data.readObject(new BufferedInputStream(new FileInputStream(file)));
+ }
+ } catch (FileNotFoundException e) {
+ // should be never thrown
+ e.printStackTrace();
+ } catch (IOException e) {
+ Activator.log.error("[SCR] Error while loading components from data file " + file.getAbsolutePath(), e);
+ } catch (Exception e) {
+ Activator.log.error("[SCR] Error while loading components from data file " + file.getAbsolutePath(), e);
+ }
+ }
+
+ public Vector loadComponentDefinitions(long bundleID) {
+ try {
+ Bundle bundle = bc.getBundle(bundleID);
+ Vector components = null;
+ if (!Activator.DBSTORE) {
+ return parseXMLDeclaration(bundle);
+ }
+
+ long lastModified;
+ // if not dev mode, we simply use the bundle's timestamp
+ if (!Activator.getBoolean(PROP_CHECK_CONFIG)) {
+ lastModified = bundle.getLastModified();
+ } else {
+ lastModified = getLastModifiedTimestamp(bundle);
+ }
+
+ dbBundlePath[0] = String.valueOf(bundleID);
+
+ String lastModifiedValue = (String) data.get(getPath(dbBundlePath));
+ if (lastModifiedValue == null) {
+ components = parseXMLDeclaration(bundle);
+ if (components != null && components.size() != 0) {
+ data.put(getPath(dbBundlePath), "" + lastModified);
+ saveComponentDefinitions(components, bundleID);
+ }
+
+ } else {
+ long dbLastModified = Long.parseLong(lastModifiedValue);
+ if (lastModified > dbLastModified) {
+ components = parseXMLDeclaration(bundle);
+ if (components != null && components.size() != 0) {
+ data.put(getPath(dbBundlePath), "" + lastModified);
+ saveComponentDefinitions(components, bundleID);
+ }
+
+ } else {
+ components = loadComponentsFromDB(bundle);
+ }
+ }
+ return components;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private Vector loadComponentsFromDB(Bundle bundle) throws Exception {
+ try {
+ ServiceComponent currentComponent = null;
+ long bundleId = bundle.getBundleId();
+ dbCompPath[0] = String.valueOf(bundleId);
+ DBObject value = new DBObject();
+ byte[] byteArr = (byte[]) data.get(getPath(dbCompPath));
+ ByteArrayInputStream tmpIn = new ByteArrayInputStream(byteArr);
+ value.readObject(tmpIn);
+ Vector components = value.components;
+ if (components == null) {
+ return null;
+ }
+ for (int i = 0; i < components.size(); i++) {
+ currentComponent = (ServiceComponent) components.elementAt(i);
+ currentComponent.bundle = bundle;
+ currentComponent.bc = bundle.getBundleContext();
+ }
+ return components;
+ } catch (Throwable t) {
+ Activator.log.error("[SCR] Error while loading components from DB", t);
+ }
+ return null;
+ }
+
+ public void deleteComponentDefinitions(long bundleID) throws Exception {
+ dbBundlePath[0] = String.valueOf(bundleID);
+ data.remove(getPath(dbBundlePath));
+ }
+
+ private void saveComponentDefinitions(Vector components, long bundleID) throws Exception {
+ try {
+ if (components == null || components.size() == 0) {
+ return;
+ }
+ dbCompPath[0] = String.valueOf(bundleID);
+
+ DBObject tmpObj = new DBObject(components);
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ tmpObj.writeObject(buf);
+ data.put(getPath(dbCompPath), buf.toByteArray());
+ saveFile();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void stop() {
+ //no body
+ }
+
+ private void saveFile() {
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(file);
+ try {
+ data.writeObject(fos);
+ fos.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private String getPath(String path[]) {
+ pathBuffer.setLength(0);
+ for (int i = 0; i < path.length; i++) {
+ pathBuffer.append(path[i]).append(separator);
+ }
+ return pathBuffer.toString();
+ }
+
+ /**
+ * The last modified timestamp of the bundle. Should only be called in development mode
+ *
+ * @param bundle
+ * @return the last modified timestamp of the bundle
+ */
+ protected long getLastModifiedTimestamp(Bundle bundle) {
+ if (bundle == null)
+ return 0;
+ long result = 0;
+ ManifestElement[] elements = parseManifestHeader(bundle);
+ for (int i = 0; i < elements.length; i++) {
+ URL componentURL = bundle.getEntry(elements[i].getValue());
+ try {
+ URLConnection connection = componentURL.openConnection();
+ long lastModified = connection.getLastModified();
+ if (lastModified > result)
+ result = lastModified;
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+ return result;
+ }
+
+ private ManifestElement[] parseManifestHeader(Bundle bundle) {
+ Dictionary headers = bundle.getHeaders();
+ String files = (String) headers.get(ComponentConstants.SERVICE_COMPONENT);
+ if (files == null)
+ return new ManifestElement[0];
+ try {
+ return ManifestElement.parseHeader(ComponentConstants.SERVICE_COMPONENT, files);
+ } catch (BundleException e) {
+ Activator.log.error("Error attempting parse manifest element header", e); //$NON-NLS-1$
+ return new ManifestElement[0];
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/Externalizable.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/Externalizable.java
new file mode 100644
index 0000000..e2e8dc4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/Externalizable.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.io;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Interface for custom serialization. Classes that implements this interface
+ * must have public empty constructor so deserialization can be done.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface Externalizable {
+
+ /**
+ * Use this method for serialization of object state.
+ */
+ public void writeObject(OutputStream oStream) throws Exception;
+
+ /**
+ * Use this method to deserializion of object state.
+ */
+ public void readObject(InputStream iStream) throws Exception;
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/ExternalizableDictionary.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/ExternalizableDictionary.java
new file mode 100644
index 0000000..b8c2cb1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/ExternalizableDictionary.java
@@ -0,0 +1,759 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.io;
+
+import java.io.*;
+import java.lang.reflect.Array;
+import java.util.*;
+
+/**
+ * This class implements a hashtable, which serves the needs of
+ * ConfigurationManagement of capsulation of configuration properties with
+ * String keys, which are case insentive at lookup (at get, remove, put
+ * operations) but preserve the last case of keys.
+ *
+ * The implementaion of the Externalizable interface allows remote transfer of
+ * those properties.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ExternalizableDictionary extends Dictionary implements Externalizable, Cloneable {
+
+ private static Class[] CLASSES = new Class[] {ExternalizableDictionary.class};
+ private HashtableEntry table[];
+ private int count;
+ private int threshold;
+ private float loadFactor;
+ private static int MIN_CAPACITY = 5;
+
+ ClassLoader loader;
+
+ /**
+ * Constructs a new, empty dictionary with the specified initial capacity
+ * and the specified load factor.
+ *
+ * @param initialCapacity
+ * the initial capacity of the hashtable. when 0 is passed for
+ * capacity, minimum possible capacity is used; i.e. when the
+ * object is read as externalizable, then the table is created
+ * with the written size.
+ * @param loadFactor
+ * a number between 0.0 and 1.0.
+ * @exception IllegalArgumentException
+ * if the initial capacity is less than zero, or if the load
+ * factor is less than or equal to zero.
+ */
+ public ExternalizableDictionary(int initialCapacity, float loadFactor) {
+ if ((initialCapacity < 0) || (loadFactor <= 0.0)) {
+ throw new IllegalArgumentException();
+ }
+ this.loadFactor = loadFactor;
+ if (initialCapacity > 0)
+ initTable(initialCapacity);
+ }
+
+ private void initTable(int initialCapacity) {
+ table = new HashtableEntry[initialCapacity];
+ threshold = (int) (initialCapacity * loadFactor);
+ }
+
+ /**
+ * Constructs a new, empty dictionary with the wanted capacity and default
+ * load factor.
+ */
+ public ExternalizableDictionary(int initialCapacity) {
+ this(initialCapacity, 0.75f);
+ }
+
+ /**
+ * Constructs a new, empty dictionary with a default capacity and load
+ * factor.
+ */
+ public ExternalizableDictionary() {
+ this(101, 0.75f);
+ }
+
+ public ClassLoader setClassLaoder(ClassLoader loader) {
+ ClassLoader tmp = this.loader;
+ this.loader = loader;
+ return tmp;
+ }
+
+ public ClassLoader setClassLoader(ClassLoader loader) {
+ ClassLoader tmp = this.loader;
+ this.loader = loader;
+ return tmp;
+ }
+
+ public Class[] remoteInterfaces() {
+ return CLASSES;
+ }
+
+ /**
+ * Gets the size of the elements in the dictionary.
+ *
+ * @return size of hashtable
+ */
+ public int size() {
+ return count;
+ }
+
+ /**
+ * Checks if there is any element in the dictionary.
+ *
+ * @return true if empty.
+ */
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ /**
+ * Gets an enumeration with dictionary's keys.
+ *
+ * @return an enumeration with dictionary's keys.
+ */
+ public synchronized Enumeration keys() {
+ return new HashtableEnumerator(table, true);
+ }
+
+ /**
+ * Gets an enumeration with dictionary's values.
+ *
+ * @return an enumeration with dictionary's values.
+ */
+ public synchronized Enumeration elements() {
+ return new HashtableEnumerator(table, false);
+ }
+
+ private int hashCode(String key) {
+ return key.toLowerCase().hashCode();
+ }
+
+ /**
+ * Gets the value corresponding to this key or any of its case
+ * representations.
+ *
+ * @param key
+ * String key
+ * @return object value or null if none
+ */
+ public synchronized Object get(Object key) {
+ if (table != null) {
+ HashtableEntry tab[] = table;
+ int hash = hashCode((String) key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ for (HashtableEntry e = tab[index]; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equalsIgnoreCase((String) key)) {
+ return e.value;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected void rehash() {
+ int oldCapacity = table.length;
+ HashtableEntry oldTable[] = table;
+
+ int newCapacity = oldCapacity * 2 + 1;
+ HashtableEntry newTable[] = new HashtableEntry[newCapacity];
+
+ threshold = (int) (newCapacity * loadFactor);
+ table = newTable;
+
+ for (int i = oldCapacity; i-- > 0;) {
+ for (HashtableEntry old = oldTable[i]; old != null;) {
+ HashtableEntry e = old;
+ old = old.next;
+ int index = (e.hash & 0x7FFFFFFF) % newCapacity;
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+ }
+
+ /**
+ * Puts the key and the value in the table. If there already is a key equal
+ * ignore case to the one passed the new value exchhanes the old one.
+ *
+ * @param key
+ * String key
+ * @param value
+ * object to put
+ * @return old value if any, or null if none
+ * @exception IllegalArgumentException
+ * if key is not a string
+ */
+ public synchronized Object put(Object key, Object value) throws IllegalArgumentException {
+
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ if (table == null)
+ initTable(MIN_CAPACITY);
+ try {
+ // Makes sure the key is not already in the hashtable.
+ int hash = hashCode((String) key);
+ int index;
+ HashtableEntry[] tab = null;
+ do {
+ tab = table;
+ index = (hash & 0x7FFFFFFF) % tab.length;
+ for (HashtableEntry e = tab[index]; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equalsIgnoreCase((String) key)) {
+ Object old = e.value;
+ e.value = value;
+ return old;
+ }
+ }
+ if (count >= threshold) {
+ // Rehash the table if the threshold is exceeded
+ rehash();
+ continue;
+ }
+ break;
+ } while (true);
+
+ // Creates the new entry.
+ HashtableEntry e = new HashtableEntry();
+ e.hash = hash;
+ e.key = (String) key;
+ e.value = value;
+ e.next = tab[index];
+ tab[index] = e;
+ count++;
+ return null;
+ } catch (ClassCastException cce) {
+ throw new IllegalArgumentException("Non string keys are not accepted!");
+ }
+ }
+
+ /**
+ * Removes the key and its corresponding value. Key is searched case
+ * insensitively.
+ *
+ * @param key
+ * string key
+ * @return object removed or null if none
+ * @exception IllegalArgumentException
+ * if key is not s string
+ */
+ public synchronized Object remove(Object key) throws IllegalArgumentException {
+
+ if (table == null)
+ return null;
+ try {
+ HashtableEntry tab[] = table;
+ int hash = hashCode((String) key);
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ for (HashtableEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
+ if ((e.hash == hash) && e.key.equalsIgnoreCase((String) key)) {
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ return e.value;
+ }
+ }
+ return null;
+ } catch (ClassCastException cce) {
+ throw new IllegalArgumentException("Non string keys are not accepted!");
+ }
+ }
+
+ /**
+ * Clears all elements from the distionary.
+ */
+ public synchronized void clear() {
+ if (table == null)
+ return;
+ for (int index = table.length; --index >= 0;) {
+ table[index] = null;
+ }
+ count = 0;
+ }
+
+ /**
+ * Creates a shallow copy of this hashtable. The keys and values themselves
+ * are not cloned.
+ *
+ * @return a new CMDictioanry with the same fields
+ */
+ public synchronized Object clone() {
+ try {
+ ExternalizableDictionary cmDict = (ExternalizableDictionary) super.clone();
+ if (table != null) {
+ cmDict.table = new HashtableEntry[table.length];
+ for (int i = table.length; i-- > 0;) {
+ cmDict.table[i] = (table[i] != null) ? (HashtableEntry) table[i].clone() : null;
+ }
+ }
+ return cmDict;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+ }
+
+ /**
+ * Compares the specified Object with this Dictionary for equality,
+ *
+ * @return true if the specified Object is equal to this Dictionary.
+ */
+
+ public synchronized boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Dictionary))
+ return false;
+ Dictionary dict = (Dictionary) o;
+ int max = size();
+ if (dict.size() != max)
+ return false;
+ Enumeration k = keys();
+ Enumeration e = elements();
+ for (int i = 0; i < max; i++) {
+ Object key = k.nextElement();
+ Object value = e.nextElement();
+ if (!value.equals(dict.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Makes a string represntation of this object.
+ *
+ * @return string represntation of this object
+ */
+ public synchronized String toString() {
+ int max = size() - 1;
+ StringBuffer buf = new StringBuffer();
+ Enumeration k = keys();
+ Enumeration e = elements();
+ buf.append('{');
+ for (int i = 0; i <= max; i++) {
+ String s1 = (String) k.nextElement();
+ String s2 = e.nextElement().toString();
+ buf.append(s1);
+ buf.append('=');
+ buf.append(s2);
+ if (i < max) {
+ buf.append(',');
+ buf.append(' ');
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ }
+
+ /**
+ * Writes this object to the stream passed. The object can be loaded again
+ * via its readObject method.
+ *
+ * @param os
+ * strream to write data to.
+ * @exception Exception
+ * if error of any kind occurs
+ */
+ public synchronized void writeObject(OutputStream os) throws Exception {
+ Enumeration keys = keys();
+ Enumeration values = elements();
+ PDataStream.writeInt(size(), os);
+ // dictionary is never empty:
+ // it either has elements or is null
+ while (keys.hasMoreElements()) {
+ PDataStream.writeUTF((String) keys.nextElement(), os);
+ writeValue(values.nextElement(), os);
+ }
+ }
+
+ private static void writeValue(Object value, OutputStream os) throws IOException {
+
+ if (value == null) {
+ os.write(-1);
+ } else {
+ Class valueClass = value.getClass();
+ // write 0 for a single value, 1 for array, and 2 for Vector
+ if (valueClass.isArray()) {
+ os.write(1);
+ int length = Array.getLength(value);
+ PDataStream.writeInt(length, os);
+ Class componentType = valueClass.getComponentType();
+ if (componentType.isPrimitive()) {
+ // primitive
+ PDataStream.writeBoolean(true, os);
+ // 1: int;2: long; 3: byte; 4: boolean; 5: character; 6:
+ // short; 7: float; 8: double
+ writePrimitiveArray(componentType, value, length, os);
+ } else {
+ PDataStream.writeBoolean(false, os);
+ PDataStream.writeUTF(componentType.getName(), os);
+ Object[] oArr = (Object[]) value;
+ for (int i = 0; i < length; i++) {
+ writeValue(oArr[i], os);
+ }
+ }
+ } else if (valueClass.equals(Vector.class)) {
+ os.write(2);
+ int size = ((Vector) value).size();
+ PDataStream.writeInt(size, os);
+ for (int i = 0; i < size; i++) {
+ writeValue(((Vector) value).elementAt(i), os);
+ }
+ } else {
+ os.write(0);
+ writeRealObject(value, valueClass, os);
+ }
+ }
+ }
+
+ private static Object readValue(InputStream is, ClassLoader loader) throws Exception {
+ byte type = (byte) is.read();
+ if (type == -1) {
+ return null;
+ }
+ Class vClass = null;
+ if (type == 2) {
+ int length = PDataStream.readInt(is);
+ Vector v = new Vector(length);
+ for (int i = 0; i < length; i++) {
+ v.insertElementAt(readValue(is, loader), i);
+ }
+ return v;
+ } else if (type == 0) {
+ return readRealObject((byte) is.read(), is, loader);
+ } else {
+ int length = PDataStream.readInt(is);
+ boolean primitive = PDataStream.readBoolean(is);
+ if (primitive) {
+ return readPrimitiveArray(length, is);
+ }
+ vClass = loader == null ? Class.forName(PDataStream.readUTF(is)) : loader.loadClass(PDataStream.readUTF(is));
+ Object array = Array.newInstance(vClass, length);
+ for (int i = 0; i < length; i++) {
+ Array.set(array, i, readValue(is, loader));
+ }
+ return array;
+ }
+ }
+
+ /**
+ * Reads the data from the InputStream and loads the data in the table.
+ *
+ * @param is
+ * stream to read dictionary's data from
+ * @exception Exception
+ * if an error of any kind occurs while reading
+ */
+ public synchronized void readObject(InputStream is) throws Exception {
+ try {
+ int size = PDataStream.readInt(is);
+ if (table == null) {
+ if (size > 0) {
+ initTable(size);
+ } else
+ initTable(MIN_CAPACITY);
+ }
+ for (int i = 0; i < size; i++) {
+ put(PDataStream.readUTF(is), readValue(is, loader));
+ }
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+
+ // 1: int;2: long; 3: byte; 4: boolean; 5: character;
+ // 6: short; 7: float; 8: double
+ private static void writePrimitiveArray(Class componentType, Object array, int length, OutputStream os) throws IOException {
+
+ if (componentType.equals(Integer.TYPE)) {
+ int[] ints = (int[]) array;
+ os.write(1);
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeInt(ints[i], os);
+ }
+ } else if (componentType.equals(Long.TYPE)) {
+ os.write(2);
+ long[] longs = (long[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeLong(longs[i], os);
+ }
+ } else if (componentType.equals(Byte.TYPE)) {
+ os.write(3);
+ os.write((byte[]) array);
+ } else if (componentType.equals(Boolean.TYPE)) {
+ os.write(4);
+ boolean[] booleans = (boolean[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeBoolean(booleans[i], os);
+ }
+ } else if (componentType.equals(Character.TYPE)) {
+ os.write(5);
+ char[] chars = (char[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeChar(chars[i], os);
+ }
+ } else if (componentType.equals(Short.TYPE)) {
+ os.write(6);
+ short[] shorts = (short[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeShort(shorts[i], os);
+ }
+ } else if (componentType.equals(Float.TYPE)) {
+ os.write(7);
+ float[] floats = (float[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeFloat(floats[i], os);
+ }
+ } else if (componentType.equals(Double.TYPE)) {
+ os.write(8);
+ double[] doubles = (double[]) array;
+ for (int i = 0; i < length; i++) {
+ PDataStream.writeDouble(doubles[i], os);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported Primitive Type: " + componentType);
+ }
+ }
+
+ private static Object readPrimitiveArray(int length, InputStream is) throws IOException {
+ byte type = (byte) is.read();
+ if (type == 1) {
+ int[] ints = new int[length];
+ for (int i = 0; i < length; i++) {
+ ints[i] = PDataStream.readInt(is);
+ }
+ return ints;
+ } else if (type == 2) {
+ long[] longs = new long[length];
+ for (int i = 0; i < length; i++) {
+ longs[i] = PDataStream.readLong(is);
+ }
+ return longs;
+ } else if (type == 3) {
+ byte[] bytes = new byte[length];
+ is.read(bytes);
+ return bytes;
+ } else if (type == 4) {
+ boolean[] booleans = new boolean[length];
+ for (int i = 0; i < length; i++) {
+ booleans[i] = PDataStream.readBoolean(is);
+ }
+ return booleans;
+ } else if (type == 5) {
+ char[] chars = new char[length];
+ for (int i = 0; i < length; i++) {
+ chars[i] = PDataStream.readChar(is);
+ }
+ return chars;
+ } else if (type == 6) {
+ short[] shorts = new short[length];
+ for (int i = 0; i < length; i++) {
+ shorts[i] = PDataStream.readShort(is);
+ }
+ return shorts;
+ } else if (type == 7) {
+ float[] floats = new float[length];
+ for (int i = 0; i < length; i++) {
+ floats[i] = PDataStream.readFloat(is);
+ }
+ return floats;
+ } else if (type == 8) {
+ double[] doubles = new double[length];
+ for (int i = 0; i < length; i++) {
+ doubles[i] = PDataStream.readDouble(is);
+ }
+ return doubles;
+ } else {
+ throw new IllegalArgumentException("Trying to read unsupported primitive type: " + type);
+ }
+ }
+
+ // only if this is an object (not primitive) and non null!
+ private static void writeRealObject(Object value, Class vClass, OutputStream os) throws IOException {
+ try {
+ if (vClass.equals(String.class)) {
+ os.write(0);
+ PDataStream.writeUTF((String) value, os);
+ } else if (vClass.equals(Integer.class)) {
+ os.write(1);
+ PDataStream.writeInt(((Integer) value).intValue(), os);
+ } else if (vClass.equals(Long.class)) {
+ os.write(2);
+ PDataStream.writeLong(((Long) value).longValue(), os);
+ } else if (vClass.equals(Byte.class)) {
+ os.write(3);
+ os.write(((Byte) value).byteValue());
+ } else if (vClass.equals(Boolean.class)) {
+ os.write(4);
+ PDataStream.writeBoolean(((Boolean) value).booleanValue(), os);
+ } else if (vClass.equals(Character.class)) {
+ os.write(5);
+ PDataStream.writeChar(((Character) value).charValue(), os);
+ } else if (vClass.equals(Short.class)) {
+ os.write(6);
+ PDataStream.writeShort(((Short) value).shortValue(), os);
+ } else if (vClass.equals(Float.class)) {
+ os.write(7);
+ PDataStream.writeFloat(((Float) value).floatValue(), os);
+ } else if (vClass.equals(Double.class)) {
+ os.write(8);
+ PDataStream.writeDouble(((Double) value).doubleValue(), os);
+ } else if (Externalizable.class.isAssignableFrom(vClass)) {
+ os.write(11);
+ String name = vClass.getName();
+ PDataStream.writeUTF(name, os);
+ Externalizable tmp = (Externalizable) value;
+ tmp.writeObject(os);
+ } else {
+ os.write(12);
+ ObjectOutputStream out = new ObjectOutputStream(os);
+ out.writeObject(value);
+ }
+ } catch (Exception exc) {
+ throw new IOException(exc.toString());
+ }
+ }
+
+ private static Object readRealObject(byte type, InputStream is, ClassLoader loader) throws IOException {
+ try {
+ if (type == 0) {
+ return PDataStream.readUTF(is);
+ } else if (type == 1) {
+ return new Integer(PDataStream.readInt(is));
+ } else if (type == 2) {
+ return new Long(PDataStream.readLong(is));
+ } else if (type == 3) {
+ return new Byte((byte) is.read());
+ } else if (type == 4) {
+ return PDataStream.readBoolean(is) ? Boolean.TRUE : Boolean.FALSE;
+ } else if (type == 5) {
+ return new Character(PDataStream.readChar(is));
+ } else if (type == 6) {
+ return new Short(PDataStream.readShort(is));
+ } else if (type == 7) {
+ return new Float(PDataStream.readFloat(is));
+ } else if (type == 8) {
+ return new Double(PDataStream.readDouble(is));
+ } else if (type == 11) {
+ String name = PDataStream.readUTF(is);
+ Class c = loader == null ? Class.forName(name) : loader.loadClass(name);
+ if (Externalizable.class.isAssignableFrom(c)) {
+ Externalizable obj = (Externalizable) c.newInstance();
+ obj.readObject(is);
+ return obj;
+ }
+ throw new IOException("Could not read object " + name);
+ } else if (type == 12) {
+ ObjectInputStream in = loader == null ? new ObjectInputStream(is) : (ObjectInputStream) new XObjectInputStream(loader, is);
+ return in.readObject();
+ }
+ } catch (ClassNotFoundException cnfe) {
+ throw new IOException("Could not find class " + cnfe.toString());
+ } catch (Exception exc) {
+ throw exc instanceof IOException ? (IOException) exc : new IOException("Could not read object " + exc.toString());
+ }
+ throw new IllegalArgumentException("Unsupported Typed Object: " + type);
+ }
+
+ /**
+ *
+ *
+ * @param props
+ * @exception IllegalArgumentException
+ */
+ public synchronized void copyFrom(Dictionary props) throws IllegalArgumentException {
+ Enumeration keys = props.keys();
+ Enumeration values = props.elements();
+ while (keys.hasMoreElements()) {
+ put(keys.nextElement(), values.nextElement());
+ }
+ }
+
+}
+
+class HashtableEntry {
+ int hash;
+ String key;
+ Object value;
+ HashtableEntry next;
+
+ protected Object clone() {
+ HashtableEntry entry = new HashtableEntry();
+ entry.hash = hash;
+ entry.key = key;
+ entry.value = value;
+ entry.next = (next != null) ? (HashtableEntry) next.clone() : null;
+ return entry;
+ }
+}
+
+class HashtableEnumerator implements Enumeration {
+ boolean keys;
+ int index;
+ HashtableEntry table[];
+ HashtableEntry entry;
+
+ HashtableEnumerator(HashtableEntry table[], boolean keys) {
+ this.table = table;
+ this.keys = keys;
+ this.index = table.length;
+ }
+
+ public boolean hasMoreElements() {
+ if (table == null)
+ return false;
+ if (entry != null) {
+ return true;
+ }
+ while (index-- > 0) {
+ if ((entry = table[index]) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object nextElement() {
+ if (table != null) {
+ if (entry == null) {
+ while ((index-- > 0) && ((entry = table[index]) == null));
+ }
+ if (entry != null) {
+ HashtableEntry e = entry;
+ entry = e.next;
+ return keys ? e.key : e.value;
+ }
+ }
+ throw new NoSuchElementException("HashtableEnumerator");
+ }
+}
+
+class XObjectInputStream extends ObjectInputStream {
+
+ ClassLoader loader;
+
+ public XObjectInputStream(ClassLoader loader, InputStream is) throws IOException {
+ super(is);
+ this.loader = loader;
+ }
+
+ protected Class resolveClass(ObjectStreamClass v) throws ClassNotFoundException {
+ return loader.loadClass(v.getName());
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/PDataStream.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/PDataStream.java
new file mode 100644
index 0000000..bdcff9b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/PDataStream.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.io;
+
+import java.io.*;
+
+/**
+ * @author Pavlin Dobrev
+ * @author Georgi Andreev
+ * @version 1.0
+ */
+public final class PDataStream {
+
+ public static void writeInt(int i, OutputStream os) throws IOException {
+ os.write((i >>> 24) & 0xFF);
+ os.write((i >>> 16) & 0xFF);
+ os.write((i >>> 8) & 0xFF);
+ os.write(i & 0xFF);
+ }
+
+ public static void writeLong(long l, OutputStream os) throws IOException {
+ os.write((int) (l >>> 56) & 0xFF);
+ os.write((int) (l >>> 48) & 0xFF);
+ os.write((int) (l >>> 40) & 0xFF);
+ os.write((int) (l >>> 32) & 0xFF);
+ os.write((int) (l >>> 24) & 0xFF);
+ os.write((int) (l >>> 16) & 0xFF);
+ os.write((int) (l >>> 8) & 0xFF);
+ os.write((int) (l & 0xFF));
+ }
+
+ public static void writeShort(short s, OutputStream os) throws IOException {
+ os.write((s >>> 8) & 0xFF);
+ os.write(s & 0xFF);
+ }
+
+ public static void writeChar(char ch, OutputStream os) throws IOException {
+ os.write((ch >>> 8) & 0xFF);
+ os.write(ch & 0xFF);
+ }
+
+ public static void writeBoolean(boolean b, OutputStream os) throws IOException {
+ os.write(b ? 1 : 0);
+ }
+
+ public static void writeByte(byte b, OutputStream os) throws IOException {
+ os.write(b);
+ }
+
+ public static void writeBytes(String str, OutputStream os) throws IOException {
+ byte[] b = str.getBytes();
+ os.write(b);
+ }
+
+ public static void writeString(String str, OutputStream os) throws IOException {
+ if (str == null) {
+ writeBoolean(false, os);
+ } else {
+ writeBoolean(true, os);
+ writeUTF(str, os);
+ }
+ }
+
+ public static void writeUTF(String str, OutputStream os) throws IOException {
+ int strlen = str.length();
+ int utflen = 0;
+ for (int i = 0; i < strlen; i++) {
+ int ch = str.charAt(i);
+ if ((ch >= 0x0001) && (ch <= 0x007F)) {
+ utflen++;
+ } else if (ch > 0x07FF) {
+ utflen += 3;
+ } else {
+ utflen += 2;
+ }
+ }
+ if (utflen > 65535)
+ throw new UTFDataFormatException();
+ os.write((utflen >>> 8) & 0xFF);
+ os.write(utflen & 0xFF);
+ for (int i = 0; i < strlen; i++) {
+ int ch = str.charAt(i);
+ if ((ch >= 0x0001) && (ch <= 0x007F)) {
+ os.write(ch);
+ } else if (ch > 0x07FF) {
+ os.write(0xE0 | ((ch >> 12) & 0x0F));
+ os.write(0x80 | ((ch >> 6) & 0x3F));
+ os.write(0x80 | (ch & 0x3F));
+ } else {
+ os.write(0xC0 | ((ch >> 6) & 0x1F));
+ os.write(0x80 | (ch & 0x3F));
+ }
+ }
+ }
+
+ public static void writeChars(String str, OutputStream os) throws IOException {
+ int len = str.length();
+ for (int i = 0; i < len; i++) {
+ int ch = str.charAt(i);
+ os.write((ch >>> 8) & 0xFF);
+ os.write(ch & 0xFF);
+ }
+ }
+
+ public static void writeDouble(double d, OutputStream os) throws IOException {
+ writeLong(Double.doubleToLongBits(d), os);
+ }
+
+ public static void writeFloat(float f, OutputStream os) throws IOException {
+ writeInt(Float.floatToIntBits(f), os);
+ }
+
+ public static int readInt(InputStream is) throws IOException {
+ int ch1 = is.read();
+ int ch2 = is.read();
+ int ch3 = is.read();
+ int ch4 = is.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0) {
+ throw new IOException("Read Error");
+ }
+ return (ch1 << 24) | (ch2 << 16) | (ch3 << 8) | ch4;
+ }
+
+ public static char readChar(InputStream is) throws IOException {
+ int ch1 = is.read();
+ int ch2 = is.read();
+ if ((ch1 | ch2) < 0)
+ throw new IOException("Read Error");
+ return (char) ((ch1 << 8) | ch2);
+ }
+
+ public static short readShort(InputStream is) throws IOException {
+ int ch1 = is.read();
+ int ch2 = is.read();
+ if ((ch1 | ch2) < 0)
+ throw new IOException("Read Error");
+ return (short) ((ch1 << 8) | ch2);
+ }
+
+ public static long readLong(InputStream is) throws IOException {
+ return ((long) (readInt(is)) << 32) | (readInt(is) & 0xFFFFFFFFL);
+ }
+
+ public static boolean readBoolean(InputStream is) throws IOException {
+ int ch = is.read();
+ if (ch < 0) {
+ throw new EOFException();
+ }
+ return (ch != 0);
+ }
+
+ public static byte readByte(InputStream is) throws IOException {
+ int ch = is.read();
+ if (ch < 0)
+ throw new EOFException();
+ return (byte) (ch);
+ }
+
+ public static int readUnsignedByte(InputStream is) throws IOException {
+ int ch = is.read();
+ if (ch < 0)
+ throw new EOFException();
+ return ch;
+ }
+
+ public static double readDouble(InputStream is) throws IOException {
+ return Double.longBitsToDouble(readLong(is));
+ }
+
+ public static float readFloat(InputStream is) throws IOException {
+ return Float.intBitsToFloat(readInt(is));
+ }
+
+ public static String readString(InputStream is) throws IOException {
+ if (readBoolean(is))
+ return readUTF(is);
+ return null;
+ }
+
+ public static String readUTF(InputStream is) throws IOException {
+ int utflen = readShort(is);
+ char str[] = new char[utflen];
+ int cnt = 0;
+ int strlen = 0;
+ while (cnt < utflen) {
+ int c = readUnsignedByte(is);
+ int char2, char3;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ case 2 :
+ case 3 :
+ case 4 :
+ case 5 :
+ case 6 :
+ case 7 :
+ cnt++;
+ str[strlen++] = (char) c;
+ break;
+ case 12 :
+ case 13 :
+ cnt += 2;
+ if (cnt > utflen)
+ throw new UTFDataFormatException();
+ char2 = readUnsignedByte(is);
+ if ((char2 & 0xC0) != 0x80)
+ throw new UTFDataFormatException();
+ str[strlen++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
+ break;
+ case 14 :
+ cnt += 3;
+ if (cnt > utflen)
+ throw new UTFDataFormatException();
+ char2 = readUnsignedByte(is);
+ char3 = readUnsignedByte(is);
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+ throw new UTFDataFormatException();
+ str[strlen++] = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F));
+ break;
+ default :
+ throw new UTFDataFormatException();
+ }
+ }
+ return new String(str, 0, strlen);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/package.html b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/package.html
new file mode 100644
index 0000000..f03fa75
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/io/package.html
@@ -0,0 +1,8 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
+</HEAD>
+<BODY LINK="#0000ff" VLINK="#800080">
+
+<P>The package contains utilities for easily transfer of objects</P></BODY>
+</HTML>
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/CharBuffer.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/CharBuffer.java
new file mode 100644
index 0000000..c0f7d6d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/CharBuffer.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.string;
+
+/**
+ * Class which stores text in a <code>char</code> array buffer
+ * and allows inserting, deleting, removing, appending etc.
+ * actions with the stored text.
+ *
+ * @author Pavlin Dobrev
+ * @author Georgi Andreev
+ * @version 1.0
+ */
+public class CharBuffer {
+
+ private char value[];
+ private int cnt;
+
+ public CharBuffer() {
+ this(16);
+ }
+
+ public CharBuffer(int len) {
+ value = new char[len];
+ }
+
+ public int length() {
+ return cnt;
+ }
+
+ public void ensureCapacity(int minCapacity) {
+ if (minCapacity > value.length)
+ expandCapacity(minCapacity);
+ }
+
+ private void expandCapacity(int minCapacity) {
+ int capacity = (value.length + 1) * 2;
+ if (minCapacity > capacity) {
+ capacity = minCapacity;
+ }
+ char newVal[] = new char[capacity];
+ System.arraycopy(value, 0, newVal, 0, cnt);
+ value = newVal;
+ }
+
+ public void setLength(int newLen) {
+ if (newLen > value.length)
+ expandCapacity(newLen);
+ if (cnt < newLen) {
+ for (; cnt < newLen; cnt++) {
+ value[cnt] = '\0';
+ }
+ } else {
+ cnt = newLen;
+ }
+ }
+
+ public char charAt(int index) {
+ if ((index < 0) || (index >= cnt))
+ throw new StringIndexOutOfBoundsException(index);
+ return value[index];
+ }
+
+ public void setCharAt(int index, char ch) {
+ if ((index < 0) || (index >= cnt))
+ throw new StringIndexOutOfBoundsException(index);
+ value[index] = ch;
+ }
+
+ public void append(String str) {
+ if (str == null)
+ str = String.valueOf(str);
+ int len = str.length();
+ int newcnt = cnt + len;
+ if (newcnt > value.length)
+ expandCapacity(newcnt);
+ str.getChars(0, len, value, cnt);
+ cnt = newcnt;
+ }
+
+ public void append(char str[], int offset, int len) {
+ int newcnt = cnt + len;
+ if (newcnt > value.length)
+ expandCapacity(newcnt);
+ System.arraycopy(str, offset, value, cnt, len);
+ cnt = newcnt;
+ }
+
+ public void append(char str[]) {
+ append(str, 0, str.length);
+ }
+
+ public void append(char c) {
+ int newcount = cnt + 1;
+ if (newcount > value.length)
+ expandCapacity(newcount);
+ value[cnt++] = c;
+ }
+
+ public String substring(int start, int end) {
+ return new String(value, start, end - start);
+ }
+
+ public String trim() {
+ int len = cnt;
+ int st = 0;
+ int off = 0;
+ char[] newVal = value;
+
+ while ((st < len) && (newVal[off + st] <= ' '))
+ st++;
+ while ((st < len) && (newVal[off + len - 1] <= ' '))
+ len--;
+
+ return ((st > 0) || (len < cnt)) ? substring(st, len) : toString();
+ }
+
+ public boolean equals(int offset, String with) {
+ int len = with.length();
+ if (offset >= cnt || offset + len != cnt)
+ return false;
+
+ for (int i = offset; i < cnt; i++) {
+ if (value[i] != with.charAt(i - offset))
+ return false;
+ }
+
+ return true;
+ }
+
+ public void getChars(int srcStart, int srcEnd, char dest[], int destStart) {
+ if ((srcStart < 0) || (srcStart >= cnt))
+ throw new StringIndexOutOfBoundsException(srcStart);
+ if ((srcEnd < 0) || (srcEnd > cnt))
+ throw new StringIndexOutOfBoundsException(srcEnd);
+
+ if (srcStart < srcEnd)
+ System.arraycopy(value, srcStart, dest, destStart, srcEnd - srcStart);
+ else if (srcStart > srcEnd)
+ throw new StringIndexOutOfBoundsException(srcEnd - srcStart);
+ }
+
+ public String toString() {
+ return new String(value, 0, cnt);
+ }
+
+ public char[] getValue() {
+ char[] retVal;
+ retVal = new char[cnt];
+ System.arraycopy(value, 0, retVal, 0, cnt);
+ return retVal;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/package.html b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/package.html
new file mode 100644
index 0000000..6dbc55c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/string/package.html
Binary files differ
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/ExTagListener.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/ExTagListener.java
new file mode 100644
index 0000000..c332b9a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/ExTagListener.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.equinox.internal.util.xml;
+
+/**
+ * The XMLReader invokes methods of this listener either when the tag is just
+ * opened or when the tag is just closed.
+ *
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface ExTagListener {
+
+ /**
+ * Invoked when a tag is opened
+ *
+ * @param aTag
+ * tag
+ * @throws IllegalArgumentException
+ */
+ public void startTag(Tag aTag) throws IllegalArgumentException;
+
+ /**
+ * Invoked when a tag is closed
+ *
+ * @param aTag
+ * tag
+ * @throws IllegalArgumentException
+ */
+ public void endTag(Tag aTag) throws IllegalArgumentException;
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/Tag.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/Tag.java
new file mode 100644
index 0000000..613fbe5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/Tag.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.util.Enumeration;
+
+/**
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface Tag {
+ /**
+ * Returns an attribute value for an attribute with a given name (aAttrName)
+ *
+ * @param aAttrName
+ * the name of attribute
+ * @return Attribute value for attribute with name given by attrName or null
+ * if there is no such an attribute
+ */
+ public String getAttribute(String aAttrName);
+
+ /**
+ * Returns an Enumeration of all the attributes' names for attributes belong
+ * to the tag object
+ *
+ * @return Enumeration that contains the attribute names of the attributes
+ * for this tag. If there is no attributes then the method returns
+ * Empty enumeration
+ */
+ public Enumeration getAttributeNames();
+
+ /**
+ * Returns an Enumeration that contains the attribute values for the
+ * attributes that belong to the tag object
+ *
+ * @return EEnumeration that contains the attribute values for the
+ * attributes that belong to the tag object. If there is no
+ * attributes then the method returns Empty enumeration
+ */
+ public Enumeration getAttributeValues();
+
+ /**
+ * Returns the name of the tag
+ *
+ * @return the name of the tag
+ */
+ public String getName();
+
+ /**
+ * Returns a tag content
+ *
+ * @return tag content. If the tag does not have a content, the method
+ * returns empty string
+ */
+ public String getContent();
+
+ /**
+ * Returns the line in the XML file where the tag definition starts
+ *
+ * @return the line in the XML file where the tag definition starts
+ */
+ public int getLine();
+
+ /**
+ * Returns the number of the child tags of this tag
+ *
+ * @return child tags number
+ */
+ public int size();
+
+ /**
+ * Returns the child tag at a given position in order that is equivalent to
+ * the order of child tags defined in the XML file
+ *
+ * @param aPosition
+ * child tag's position
+ * @return the child tag at a given position
+ */
+ public Tag getTagAt(int aPosition);
+
+ /**
+ * Returns the content of a child tag with index position equals to aPos but
+ * only if its name is equal to aName. Otherwise the method throws an
+ * exception
+ *
+ * @param aPos
+ * index of the tag in its parent list
+ * @param aName
+ * the name of the tag
+ * @return tag content
+ * @throws NullPointerException
+ * if there is no tag on the given position
+ * @throws IllegalArgumentException
+ * if the name of the tag on the given position does not match
+ * the requested name
+ */
+ public String getContent(int aPos, String aName);
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagClass.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagClass.java
new file mode 100644
index 0000000..8634d65
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagClass.java
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.util.Vector;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.util.string.CharBuffer;
+
+/**
+ * Class corresponding to one XML tag, containing its tag name, attributes,
+ * content string and tree of the sub tags.
+ *
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class TagClass {
+
+ private static final String INTERN_TAGCONTENT = "xml.intern.tagcontent";
+ private static boolean fInternTagContent = Activator.getBoolean(INTERN_TAGCONTENT);
+
+ private CharBuffer fContent = null;
+ private String fName = null;
+ private Vector fTags = null;
+ String fAttributes = null;
+ private int fLine = -1;
+ protected boolean fInline = false;
+
+ /**
+ * Creates an empty tag node
+ */
+ public TagClass() {
+ // Do nothing here
+ }
+
+ /**
+ * Creates tag node with specified name
+ *
+ * @param name
+ * tag name
+ */
+ public TagClass(String name) {
+ fName = name;
+ }
+
+ /**
+ * Creates tag node with specified name
+ *
+ * @param line
+ * the line in the XML file where the tags begin.
+ * @param name
+ * tag name
+ */
+ public TagClass(int line, String name) {
+ fLine = line;
+ fName = name;
+ }
+
+ /**
+ * Creates tag node with specified name and content string
+ *
+ * @param name
+ * tag name
+ * @param contentStr
+ * content string of the tag
+ */
+ public TagClass(String name, String contentStr) {
+ setup(-1, name, contentStr);
+ }
+
+ /**
+ * Creates tag node with specified name and content string
+ *
+ * @param line
+ * the line in the XML file where the tags begin.
+ * @param name
+ * tag name
+ * @param contentStr
+ * content string of the tag
+ */
+ public TagClass(int line, String name, String contentStr) {
+ setup(line, name, contentStr);
+ }
+
+ private void setup(int line, String name, String contentStr) {
+ fLine = line;
+ fName = name;
+ if (contentStr != null && contentStr.length() > 0) {
+ fContent = new CharBuffer(contentStr.length());
+ fContent.append(contentStr);
+ }
+ }
+
+ /**
+ * Appends the 'toAppend' CharBuffer content to the content buffer
+ *
+ * @param toAppend
+ * CharBuffer content to append
+ */
+ protected void appendContent(CharBuffer toAppend) {
+ if (fContent == null) {
+ fContent = new CharBuffer();
+ }
+ fContent.append(toAppend.getValue());
+ }
+
+ /**
+ * Appends the 'toAppend' string to the content buffer
+ *
+ * @param toAppend
+ * string to append
+ */
+ protected void appendContent(String toAppend) {
+ if (fContent == null) {
+ fContent = new CharBuffer();
+ }
+ fContent.append(toAppend);
+ }
+
+ /**
+ * Returns content string of the tag
+ *
+ * @return tag content note: The content that is to be returned will be
+ * trimmed first
+ */
+ /*
+ * Implementation Note: The content string is not suitable to be interned
+ * with String().intern() because it is more often to be unique and the
+ * benefit will be much smaller than expected.
+ */
+ public String getContent() {
+ String result = "";
+ if (fContent != null) {
+ result = fContent.trim();
+ if (fInternTagContent) {
+ result = result.intern();
+ }
+ }
+ return result;
+ // TODO -oIvan: Should not it be null instead of empty string?...
+ // Plamen: Yes it have to be null insted of "" because the old one used
+ // to return null.
+ // Ivan Dimitrov: The metatyping code relies on the fact that the
+ // content is never null
+ }
+
+ /**
+ * Returns the content of a child tag with index position equals to aPos but
+ * only if its name is equal to aName. Otherwise the method throws an
+ * exception
+ *
+ * @param aPos
+ * index of the tag in its parent list
+ * @param aName
+ * the name of the tag
+ * @return tag content
+ * @throws NullPointerException
+ * if there is no tag on the given position
+ * @throws IllegalArgumentException
+ * if the name of the tag on the given position does not match
+ * the requested name
+ */
+ public String getContent(int aPos, String aName) {
+ TagClass child = getTagAt(aPos);
+ if (child == null)
+ throw new NullPointerException("There is no such a tag. [Parent tag name] = [" + aName + "], [child tag index] = " + aPos + ", [child tag name] = [" + aName + ']');
+
+ if (child.getName().equals(aName))
+ return child.getContent();
+
+ throw new IllegalArgumentException("There is no such a tag. [Parent tag name] = [" + aName + "], [child tag index] = " + aPos + ", [child tag name] = [" + aName + ']');
+ }
+
+ /**
+ * Returns the internal content buffer
+ *
+ * @return internal content buffer as CharBuffer
+ */
+ protected CharBuffer getContentBuffer() {
+ if (fContent == null) {
+ fContent = new CharBuffer();
+ }
+ return fContent;
+ }
+
+ /**
+ * Returns name of the tag
+ *
+ * @return tag name
+ */
+ public String getName() {
+ return fName;
+ }
+
+ /**
+ * Sets the name of the tag
+ *
+ * @param aName
+ * name of the tag
+ */
+ protected void setName(String aName) {
+ fName = aName;
+ }
+
+ /**
+ * Returns string with all attribute names and values of the tag, as is in
+ * the XML content.
+ *
+ * @return tag name and its attributes
+ */
+ public String getAttributes() {
+ return (fAttributes != null) ? fAttributes : "";
+ }
+
+ /**
+ * Returns an attribute value for an attribute with a given name (attrName)
+ *
+ * @param attrName
+ * the name of attribute
+ * @return Attribute value for attribute with name given by attrName or null
+ * if there is no such an attribute
+ * @throws NullPointerException
+ * if attrName is null
+ */
+ public String getAttribute(String attrName) {
+ return XMLUtil.getAttributeValue(fAttributes, attrName, 0);
+ }
+
+ /**
+ * Adds a child tag to this one
+ *
+ * @param aTag
+ * a tag to be added as a child tag
+ */
+ public void addTag(TagClass aTag) {
+ if (fTags == null) {
+ fTags = new Vector(5, 5);
+ }
+ fTags.addElement(aTag);
+ }
+
+ /**
+ * Returns sub tag of this one at a given position
+ *
+ * @param aPos
+ * position of the searched tag
+ * @return a child tag at a given position in the child-tags set or null if
+ * there is no such a tag
+ */
+ public TagClass getTagAt(int aPos) {
+ return (fTags == null) ? null : (TagClass) fTags.elementAt(aPos);
+ }
+
+ /**
+ * Returns children tags count
+ *
+ * @return sub tags count
+ */
+ public int size() {
+ return (fTags == null) ? 0 : fTags.size();
+ }
+
+ /**
+ * Returns the line in the XML file where the tags begin.
+ *
+ * @return Returns the line.
+ */
+ public int getLine() {
+ return fLine;
+ }
+
+ /**
+ * Sets the tag line number (the line in the XML file where the current tag
+ * starts)
+ *
+ * @param aLine
+ * current tag line number
+ */
+ protected void setLine(int aLine) {
+ fLine = aLine;
+ }
+
+ protected void setInline() {
+ fInline = true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagListener.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagListener.java
new file mode 100644
index 0000000..df623e7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/TagListener.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+/**
+ * This interface is used while parsing xml files. It's method is invoked on
+ * every closing tag.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface TagListener {
+
+ /**
+ * Invoked when a tag has been closed
+ *
+ * @param tag
+ * tag with its content and its subtags
+ * @throws IllegalArgumentException
+ * may be thrown while proccessing the tag structure
+ */
+ public void useTag(TagClass tag) throws IllegalArgumentException;
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLParser.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLParser.java
new file mode 100644
index 0000000..86c7606
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLParser.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.io.*;
+import org.eclipse.equinox.internal.util.xml.impl.XMLParserImpl;
+
+/**
+ * <p>
+ * Class used for reading of xml files, creating tree structure of 'TagClass'
+ * for each xml tag. When reader reaches a closed tag it notifies a given
+ * 'TagListener' and sends the last tag to it. If closing tag does not
+ * correspond with the last open IllegalArgumentException is thrown. There is a
+ * debug property 'xml.debug' used to dump an Exceptions occurred while
+ * operation is running.
+ * </p>
+ *
+ * <p>
+ * The parser, in general, is a simple XML parser that implements
+ * "Recursive descent" parsing method.
+ *
+ * Known limitations:<br>
+ *
+ * <pre>
+ * Currently this XMLParser does not support the following special tags:
+ * 1. <?TAG_NAME ..... ?> or also "Processing Instructions"
+ * 2. <!DOCTYPE .... >
+ * 3. <!ELEMENT .... >
+ * 4. <!ATTLIST .... >
+ * 5. <!ENTITY .... >
+ * </pre>
+ *
+ * <br>
+ * The parser skippes these tags (it searches for '>' symbol and closes the
+ * 'special' tag).<br>
+ *
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class XMLParser {
+
+ /**
+ * Parses a XML file given through aInputStream and during the parsing
+ * notifies aListener for close-tag and open-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on close-tag and open-tag
+ * events
+ * @param aLevel
+ * indicates the tag level that the listener will be invoked for.
+ * For example if the XML is:<br>
+ *
+ * <pre>
+ * <a>
+ * <b>
+ * <c />
+ * </b>
+ * </a>
+ * </pre>
+ *
+ * <br>
+ * and the passed aLevel is 2 then the listener will be invoked
+ * only for tags that have level 2, i.e. in our example the
+ * listener will be invoked only for tag <b><br>
+ * <ul>
+ * <li>Value less than 0 indicates "invoke listener for all
+ * tags no matter what are their levels"</li>
+ * <li>Value of 0 indicates that the listener must not be
+ * invoked in general no matter what is the tag level</li>
+ * <li>Value greater than 0 indicates the tag level that the
+ * listener will be invoked for</li>
+ * description
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(InputStream aInputStream, ExTagListener aListener, int aLevel) throws IOException {
+ XMLParserImpl xml = new XMLParserImpl(aInputStream, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+
+ /**
+ * Parses a XML file given through aReader and during the parsing notifies
+ * aListener for close-tag and open-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * aReader to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on close-tag and open-tag
+ * events
+ * @param aLevel
+ * see parseXML(Reader aReader, TagListener aListener, int aLevel
+ * description
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(Reader aReader, ExTagListener aListener, int aLevel) throws IOException {
+ XMLParserImpl xml = new XMLParserImpl(aReader, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLReader.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLReader.java
new file mode 100644
index 0000000..c0de29d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLReader.java
@@ -0,0 +1,1329 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.io.*;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.util.string.CharBuffer;
+
+/**
+ * <p>
+ * Class used for reading of xml files, creating tree structure of 'TagClass'
+ * for each xml tag. When reader reaches a closed tag it notifies a given
+ * 'TagListener' and sends the last tag to it. If closing tag does not
+ * correspond with the last open IllegalArgumentException is thrown. There is a
+ * debug property 'xml.debug' used to dump an Exceptions occurred while
+ * operation is running.
+ * </p>
+ *
+ * <p>
+ * The parser, in general, is a simple XML parser that implements
+ * "Recursive descent" parsing method.
+ *
+ * Known limitations:<br>
+ *
+ * <pre>
+ * Currently this XMLParser does not support the following special tags:
+ * 1. <?TAG_NAME ..... ?> or also "Processing Instructions"
+ * 2. <!DOCTYPE .... >
+ * 3. <!ELEMENT .... >
+ * 4. <!ATTLIST .... >
+ * 5. <!ENTITY .... >
+ * </pre>
+ *
+ * <br>
+ * The parser skippes these tags (it searches for '>' symbol and closes the
+ * 'special' tag).<br>
+ *
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class XMLReader {
+
+ private static final String DEBUG = "equinox.ds.xml.debug";
+ private static final String SET_OLD_BEHAVIOUR = "equinox.ds.xml.oldbehaviour";
+ private static final String SET_OLD_LEVELS = "equinox.ds.xml.oldlevels";
+ private static final String INTERN_ATTRIBUTES = "equinox.ds.xml.intern.attributes";
+
+ private static final String CDATA = "CDATA";
+ private static final String XML = "xml";
+ private static final String VERSION = "version";
+ private static final String ENCODING = "encoding";
+ private static final String STANDALONE = "standalone";
+
+ private static final String ERR_EOS = "End-of-stream reached before the end of XML.";
+ private static final String ERR_ENTITY_EXPECTED = "Entity reference or Character reference expected.";
+ private static final String ERR_EQUAL_EXPECTED = "'=' expected.";
+ private static final String ERR_QUOT_EXPECTED = "''' or '\"' expected.";
+ private static final String ERR_GT_EXPECTED = "'>' expected.";
+ private static final String ERR_LT_EXPECTED = "'<' expected.";
+ private static final String ERR_CLOSE_TAG1_EXPECTED = "'/' or tag name expected.";
+ private static final String ERR_CLOSE_TAG2_EXPECTED = "'>', '/>' or more attributes expected.";
+ private static final String ERR_CLOSE_TAG3_EXPECTED = "'?>' expected.";
+ private static final String ERR_CONTENT_EXPECTED = "Content data, new tag or closing tag expected.";
+ private static final String ERR_QUESTIONMARK_EXPECTED = "'?' expected.";
+ private static final String ERR_ILLEGAL_CHARACTER = "Illegal character.";
+ private static final String ERR_TAGNAME_EXPECTED = "Tag name expected.";
+ private static final String ERR_TAGNAME2_EXPECTED = "Tag name, '?' or '!' expected.";
+ private static final String ERR_DASH_EXPECTED = "'-' expected.";
+ private static final String ERR_COMMENT_CLOSE_EXPECTED = "'-->' expected.";
+ private static final String ERR_CDATA_EXPECTED = "'CDATA' expected.";
+ private static final String ERR_OPENSQBRACKET_EXPECTED = "'[' expected.";
+ private static final String ERR_CLOSE_CDATA_EXPECTED = "']]>' expected.";
+ private static final String ERR_SEMICOLON_EXPECTED = "';' expected.";
+ private static final String ERR_XMLPROLOG_EXPECTED = "XML prolog '<?xml' is not expected at this position.";
+ private static final String ERR_VERSION_EXPECTED = "'version' attribute expected.";
+ private static final String ERR_ENCODING_STANDALONE_EXPECTED = "'encoding', 'standalone' or '?>' expected.";
+ private static final String ERR_STANDALONE_EXPECTED = "'standalone' attribute expected.";
+
+ private static final boolean fDebug = Activator.getBoolean(DEBUG);
+ private static final boolean fOldBehaviour = Activator.getBoolean(SET_OLD_BEHAVIOUR);
+ private static final boolean fOldLevels = Activator.getBoolean(SET_OLD_LEVELS);
+ private static final boolean fInternAttributes = Activator.getBoolean(INTERN_ATTRIBUTES);
+
+ private String fDefaultEncoding = "UTF-8";
+
+ // private CharBuffer c;
+ private CharBuffer temp = new CharBuffer();
+ private CharBuffer temp2 = null;
+
+ protected Reader fReader = null;
+ protected InputStream fStream = null;
+
+ protected char currentChar = 0;
+ protected TagListener fTagListener;
+
+ protected int fLine = 1;
+ protected int fPos = 0;
+
+ protected int fLevel = -1;
+ protected int fCurrentLevel = 1;
+
+ private String fVersion = "1.0";
+ private String fEncoding = "UTF-8";
+ private String fStandalone = "no";
+
+ protected static final String[] fNew_entities = {"amp", "apos", "lt", "gt", "quot"};
+ protected static final char[] fNew_ent_chars = {'&', '\'', '<', '>', '"'};
+
+ protected static final String[] fOld_entities = {"amp", "nbsp", "crlf", "tab", "lt", "gt", "quot", "apos"};
+ protected static final char[] fOld_ent_chars = {'&', ' ', '\n', '\t', '<', '>', '"', '\''};
+
+ /**
+ * An empty default constructor
+ */
+ public XMLReader() {
+ //
+ }
+
+ /**
+ * Constructs a new XMLReader. <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on tag close event
+ * @throws IOException
+ */
+ public XMLReader(InputStream aInputStream, TagListener aListener) {
+ fStream = aInputStream;
+ fTagListener = aListener;
+ }
+
+ /**
+ * Constructs a new XMLReader. <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * a reader that will be used to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on tag close event
+ */
+ public XMLReader(Reader aReader, TagListener aListener) {
+ fReader = aReader;
+ fTagListener = aListener;
+ }
+
+ /**
+ * Parses a XML file given through aInputStream and during the parsing
+ * notifies aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag events
+ * @throws IOException
+ */
+ public static void read(InputStream aInputStream, TagListener aListener) throws IOException {
+ parseXML(aInputStream, aListener, -1);
+ }
+
+ /**
+ * Parses a XML file given through aInputStream and during the parsing
+ * notifies aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag events
+ * @param aLevel
+ * see parseXML(Reader aReader, TagListener aListener, int aLevel
+ * description
+ * @throws IOException
+ */
+ public static void read(InputStream aInputStream, TagListener aListener, int aLevel) throws IOException {
+ parseXML(aInputStream, aListener, aLevel);
+ }
+
+ /**
+ * Parses a XML file given through aReader and during the parsing notifies
+ * aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * a Reader to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag events
+ * @throws IOException
+ */
+ public static void read(Reader aReader, TagListener aListener) throws IOException {
+ parseXML(aReader, aListener, -1);
+ }
+
+ /**
+ * Parses a XML file given through aReader and during the parsing notifies
+ * aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * a Reader to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag events
+ * @param aLevel
+ * see parseXML(Reader aReader, TagListener aListener, int aLevel
+ * description
+ * @throws IOException
+ */
+ public static void read(Reader aReader, TagListener aListener, int aLevel) throws IOException {
+ parseXML(aReader, aListener, aLevel);
+ }
+
+ /**
+ * Sets the parser's encoding. If there is a current encoding associated
+ * with the parser the method returns immediately
+ *
+ * @param aEncoding
+ * new encoding to be set
+ * @throws UnsupportedEncodingException
+ * if the encoding is not supported.
+ */
+ protected void setEncoding(String aEncoding) {
+ if (fReader == null) {
+ try {
+ fReader = new InputStreamReader(fStream, aEncoding);
+ } catch (Exception e) {
+ if (fDebug) {
+ System.err.println("[XMLReader] Failed setting the encoding \"" + aEncoding + "\", continue parsing with the default one.");
+ }
+ fReader = new InputStreamReader(fStream);
+ }
+ }
+ }
+
+ /**
+ * Sets the level of tags bellow which the listener will be notified for.
+ * For internal use only.
+ *
+ * @param aLevel
+ */
+ protected void setLevel(int aLevel) {
+ fLevel = aLevel;
+ }
+
+ /* A helper function to reuse a temp CharBuffers without recreating it */
+ protected CharBuffer getCharBuffer() {
+ if (temp.length() <= 0) {
+ return temp;
+ } else if (temp2 == null) {
+ temp2 = new CharBuffer(0);
+ return temp2;
+ } else if (temp2.length() <= 0) {
+ return temp2;
+ }
+ return new CharBuffer(0);
+ }
+
+ protected char prev_char = 0;
+ protected char[] fBuffer = new char[4096];
+ protected int fBufferLen = 0;
+ protected int fBufferPos = 0;
+
+ /**
+ * Reads the next char from the input stream and sets it to private field
+ * <code>currentChar</code>
+ *
+ * @return true if the next char is successfully read of false if
+ * End-Of-Stream is reached
+ * @throws IOException
+ * if some error occurs during reading the character or if the
+ * caller tries to read beyond the End-Of-Stream.
+ */
+ protected boolean getNextChar() throws IOException {
+ // // Reading characters without buffering
+ // int ichar = 0;
+ // int count = 0;
+ // while (ichar == 0 && count < 100) {
+ // ichar = fReader.read();
+ // count++;
+ // }
+ //
+ // if (ichar == 0)
+ // throw new IOException("Failed to read from the input file.");
+ //
+ // if (ichar < 0 && prev_char == 0)
+ // throw new IOException(ERR_EOS);
+ //
+ // char ch = (char) ichar;
+
+ char ch;
+
+ if (fReader == null) { // If there is no associated reader,
+ int ach = fStream.read(); // then reads from the InputStream until
+ if (ach < 0) { // the rigth encoding is recognized
+ ach = 0;
+ }
+
+ ch = (char) ach;
+ if (ch == 0 && prev_char == 0) {
+ throw new IOException(ERR_EOS);
+ }
+ } else {
+ if (fBufferLen < 0) {
+ throw new IOException(ERR_EOS);
+ }
+
+ if ((fBufferPos) >= fBufferLen) {
+ // Refetch the buffer
+ fBufferLen = 0;
+ fBufferPos = 0;
+ int count = 0;
+ while (fBufferLen == 0 && count < 100) {
+ fBufferLen = fReader.read(fBuffer);
+ count++;
+ }
+
+ ch = (fBufferLen > 0) ? fBuffer[fBufferPos++] : 0;
+
+ if (fBufferLen == 0) {
+ fBufferLen = -1;
+ }
+ } else {
+ ch = fBuffer[fBufferPos++];
+ }
+ }
+
+ prev_char = currentChar;
+ currentChar = ch;
+ fPos++;
+
+ switch (ch) {
+ case '\n' :
+ if (prev_char != '\r') {
+ fLine++;
+ }
+ fPos = 0;
+ break;
+ case '\r' :
+ fPos = 0;
+ fLine++;
+ break;
+ }
+ return (currentChar != 0);
+ }
+
+ /**
+ * Parses the attribute value and if it's successful then adds it to the
+ * CharBuffer. If there are EntityReferences of CharReferences in the
+ * attribute value, they will be turned to their equivalent symbols.<br>
+ * attr_value ::= (acceptable_char | EntityRef | CharRef)* - quot_symbol
+ *
+ * @see parse_attr
+ * @see parse_CharRef
+ * @see parse_EntityRef
+ */
+ protected void parse_attr_value(CharBuffer sb, char quot) throws IOException {
+ while (currentChar != quot && currentChar != '<') {
+ if (accept_char('&')) {
+ if (!parse_CharRef(sb)) {
+ if (!parse_EntityRef(sb, true)) {
+ err(fPos - 1, ERR_ENTITY_EXPECTED);
+ }
+ }
+ } else {
+ sb.append(currentChar);
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute with the given simplified grammar:<br>
+ *
+ * <pre>
+ * attribute ::= S* + attr_name + S* + '=' + S* + ('\'' + (attr_value - '\'') + '\'')) | ('"' + (attr_value - '"') + '"'))
+ * attr_value ::= (acceptable_char | EntityRef | CharRef)*
+ * attr_name ::= identifier
+ * </pre>
+ *
+ * @param aParent
+ * the parent tag where the correctly parsed attribute will be
+ * added
+ * @throws IOException
+ * @see parse_identifier
+ * @see parse_attr_value
+ */
+ protected boolean parse_attr(CharBuffer cb) throws IOException {
+ clearWhiteSpaces();
+
+ cb.append(' ');
+ int length = parse_identifier(cb);
+
+ if (length > 0) {
+ clearWhiteSpaces();
+
+ if (!accept_char('=')) {
+ err(ERR_EQUAL_EXPECTED);
+ }
+
+ cb.append('=');
+ clearWhiteSpaces();
+
+ char quot = 0;
+ if (accept_char('"')) {
+ quot = '"';
+ } else if (accept_char('\'')) {
+ quot = '\'';
+ } else {
+ err(ERR_QUOT_EXPECTED);
+ }
+
+ cb.append(quot);
+ parse_attr_value(cb, quot);
+
+ if (!accept_char(quot)) {
+ err("'" + quot + "' expected.");
+ }
+
+ cb.append(quot);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parses a tag attribute list with the following simplified grammar:
+ *
+ * <pre>
+ * attr_list ::= attribute*
+ * @param aParent the parent tag that the parsed attributes will be added to
+ * @return true if at least one attribute is parsed correctly and false otherwise
+ * @throws IOException
+ * @see parse_attr
+ *
+ */
+ protected boolean parse_attr_list(TagClass aParent) throws IOException {
+ boolean result = false;
+
+ CharBuffer cb = getCharBuffer();
+ cb.append(aParent.getName());
+ while (parse_attr(cb)) {
+ result = true;
+ }
+
+ aParent.fAttributes = ((fInternAttributes) ? cb.toString().intern() : cb.toString());
+ cb.setLength(0);
+ return result;
+ }
+
+ private static final char bA = 'A' - 1;
+ private static final char aZ = 'Z' + 1;
+ private static final char ba = 'a' - 1;
+ private static final char az = 'z' + 1;
+ private static final char b0 = '0' - 1;
+ private static final char a9 = '9' + 1;
+
+ /**
+ * This method returns true is the passed character may be used as starting
+ * character for tag name and attribute name
+ *
+ * @param ch
+ * the tested character
+ * @return true if the character could be used as starting character for a
+ * tag name and an attribute name and false otherwise
+ */
+ public final static boolean isNameStartChar(char ch) {
+ return (ch > bA && ch < aZ) || (ch > ba && ch < az) || (ch == ':') || (ch == '_') || (ch > 0xBF && ch < 0xD7) || (ch > 0xD7 && ch < 0xF7) || (ch > 0xF7 && ch < 0x300) || (ch > 0x36F && ch < 0x37E) || (ch > 0x37E && ch < 0x2000) || (ch > 0x200B && ch < 0x200E) || (ch > 0x206F && ch < 0x2190) || (ch > 0x2BFF && ch < 0x2FF0) || (ch > 0x3000 && ch < 0xD800) || (ch > 0xF900 && ch < 0xFDD0) || (ch > 0xFDEF && ch < 0xFFFE) || (ch > 0x0FFFF && ch < 0xF0000);
+ }
+
+ /**
+ * This method returns true if the passed characted may be used as part of a
+ * tag name or an attribute name
+ *
+ * @param ch
+ * the tested character
+ * @return true is the characted could be used as part of a tag name or an
+ * attribute name and false otherwise
+ */
+ public final static boolean isNameChar(char ch) {
+ return (ch == '-') || (ch == '.') || (ch == 0xB7) || (ch > b0 && ch < a9) || isNameStartChar(ch) || (ch > 0x02FF && ch < 0x0370) || (ch > 0x203E && ch < 0x2041);
+ }
+
+ /**
+ * Parses an identifier.
+ *
+ * @return an identifier as a string if it is parsed successfully and null
+ * otherwise
+ * @throws IOException
+ * if an exception occurs during read operations from the Reader
+ * or the InputStream
+ */
+ protected String parse_identifier() throws IOException {
+ if (isNameStartChar(currentChar)) {
+ CharBuffer sb = getCharBuffer();
+
+ while (isNameChar(currentChar)) {
+ sb.append(currentChar);
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ String result = sb.toString().intern();
+ sb.setLength(0);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * Parses an identifier and places it into the passed CharBuffer
+ *
+ * @param cb
+ * CharBuffer where the parsed identifier will be placed into
+ * @return the length of the parsed identifier
+ * @throws IOException
+ * if an exception occurs during read operations from the Reader
+ * or the InputStream
+ */
+ protected int parse_identifier(CharBuffer cb) throws IOException {
+ if (isNameStartChar(currentChar)) {
+ int length = 0;
+ while (isNameChar(currentChar)) {
+ cb.append(currentChar);
+ length++;
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+
+ return length;
+ }
+ return 0;
+ }
+
+ /**
+ * Parses a tag name and if it is successfully parsed the method sets it as
+ * a name of the parent tag
+ *
+ * @param aParent
+ * parent tag
+ * @return true if the name is parsed successfully and false otherwise
+ * @throws IOException
+ * @see parse_identifier
+ */
+ protected boolean parse_tag_name(TagClass aParent) throws IOException {
+ String name = parse_identifier();
+ if (name != null) {
+ aParent.setName(name);
+ }
+ return name != null;
+ }
+
+ /**
+ * Helper function that notify listeners depending on certain conditions
+ * such as if the tag event is on-close or on-open
+ *
+ * @param aTag
+ * The tag that the notification event is valid for.
+ * @param isStart
+ * true if the event is on-open and false if it is on-close
+ */
+ protected void notifyListeners(TagClass aTag) {
+ try {
+ if (fLevel <= 0 || fLevel == fCurrentLevel) {
+ fTagListener.useTag(aTag);
+ }
+ } catch (RuntimeException re) {
+ if (fDebug) {
+ System.err.println("An outside exception occured while processing a tag on line " + aTag.getLine() + ", the tag name is: " + aTag.getName() + ", the level is: " + fCurrentLevel);
+ re.printStackTrace(System.err);
+ }
+ throw re;
+ }
+ }
+
+ /**
+ * Parses a normal tag. There are two cases - (1) the tag has separate open
+ * and close tag elements and (2) the tag is simple suchas <tag_name ...
+ * />
+ *
+ * @param aParent
+ * The parent tag that this tag will be added to if the parsing
+ * is successful
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see clearWhiteSpaces
+ * @see parse_tag_name
+ * @see parse_attr_list
+ * @see notifyListeners
+ * @see accept_char
+ * @see accept_seq
+ * @see parse_PCDATA
+ */
+ protected boolean parse_tag_normal(TagClass aParent) throws IOException {
+ // Looking for a tag_name (identifier)
+ if (isNameStartChar(currentChar)) {
+ TagClass tag = new TagClass();
+ tag.setLine(fLine);
+
+ parse_tag_name(tag);
+ parse_attr_list(tag);
+
+ clearWhiteSpaces();
+
+ if (accept_char('/')) {
+ if (!accept_char('>')) {
+ err(ERR_GT_EXPECTED);
+ }
+ tag.setInline();
+ aParent.addTag(tag);
+
+ if (!fOldBehaviour) {
+ notifyListeners(tag);
+ }
+
+ return true;
+ } else if (accept_char('>')) {
+
+ while (true) {
+ clearWhiteSpaces();
+ int pos = fPos;
+ if (currentChar == '<') { // Normal tag, Special tag or
+ // closing tag
+ if (!parse_tag(tag)) { // It may be a special tag.
+ if (!accept_char('/')) {
+ err(pos + 1, ERR_CLOSE_TAG1_EXPECTED);
+ }
+
+ // trying to accept: tag_name + S* + '>'
+ pos = fPos;
+ if (!accept_seq(tag.getName())) {
+ err(pos, '\'' + tag.getName() + "' string expected.");
+ }
+
+ clearWhiteSpaces();
+ if (!accept_char('>')) {
+ err(ERR_GT_EXPECTED);
+ }
+
+ aParent.addTag(tag);
+
+ notifyListeners(tag);
+
+ return true;
+ }
+ } else {
+ if (!parse_PCDATA(tag))
+ break;
+ }
+ }
+ err(ERR_CONTENT_EXPECTED);
+ } else {
+ err(ERR_CLOSE_TAG2_EXPECTED);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parses special tags, such that begins with:<br>
+ *
+ * <pre><code>
+ * <!-- comments
+ * <!tag_name Parsing instructions
+ * <![ CDATA element
+ * <? DOCTYPE, etc.
+ * </code></pre>
+ *
+ * @param aParent
+ * The parent tag that this tag will be added to if the parsing
+ * is successful
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ * @see clearWhiteSpaces
+ * @see parse_tag_CDATA
+ * @see parse_tag_name
+ * @see parse_comment
+ */
+ protected boolean parse_tag_special(TagClass aParent) throws IOException {
+ if (accept_char('!')) {
+
+ TagClass tag = new TagClass();
+
+ if (parse_tag_name(tag)) {
+ clearWhiteSpaces();
+
+ while (true) {
+ if (accept_char('>')) {
+ clearWhiteSpaces();
+ return true;
+ }
+ getNextChar();
+ }
+ } else if (parse_tag_CDATA(aParent)) { // parse CDATA tag
+ return true;
+ } else if (parse_comment(tag)) {
+ return true;
+ }
+ } else if (accept_char('?')) {
+ TagClass tag = new TagClass();
+
+ int pos = fPos;
+ if (parse_tag_name(tag)) {
+ if (tag.getName().equals(XML)) {
+ err(pos - 2, ERR_XMLPROLOG_EXPECTED);
+ }
+
+ char prevCh = 0;
+ while (true) {
+ if (currentChar == '>') {
+ if (prevCh == '?') {
+ accept_char('>');
+ clearWhiteSpaces();
+ return true;
+ }
+ }
+ prevCh = currentChar;
+ getNextChar();
+ }
+
+ }
+ err(pos, ERR_TAGNAME_EXPECTED);
+ }
+ return false;
+ }
+
+ /**
+ * Parses an attribute value and returns it as a string
+ *
+ * @return the parsed attribute value as a string.
+ * @throws IOException
+ * if an exception occurs during read operations from the Reader
+ * or the InputStream
+ */
+ protected String getAttrValue() throws IOException {
+ CharBuffer cb = getCharBuffer();
+
+ clearWhiteSpaces();
+ accept_char('=');
+ clearWhiteSpaces();
+
+ if (currentChar != '\'' && currentChar != '"') {
+ err(ERR_QUOT_EXPECTED);
+ }
+
+ char quot = currentChar;
+ accept_char(quot);
+ parse_attr_value(cb, quot);
+
+ if (!accept_char(quot)) {
+ err("'" + quot + "' expected.");
+ }
+
+ String result = cb.toString();
+ cb.setLength(0);
+ clearWhiteSpaces();
+ return result;
+ }
+
+ /**
+ * Parses the XML prolog tag, i.e.<br>
+ * <code> <?xml version="..." encoding="..." standalone="..." ?> </code><br>
+ *
+ * @param parent
+ * the parent tag (in this case this is the root "fake" tag,
+ * which the listeners will never be informed for...)
+ * @throws IOException
+ * if an exception occurs during read operations from the Reader
+ * or the InputStream
+ */
+ protected boolean parse_xml_prolog(TagClass parent) throws IOException {
+ if (accept_char('?')) {
+ TagClass tag = new TagClass();
+
+ if (parse_tag_name(tag)) {
+ if (tag.getName().equalsIgnoreCase(XML)) {
+ if (fOldLevels)
+ fCurrentLevel++;
+
+ clearWhiteSpaces();
+
+ int pos = fPos;
+
+ String s = parse_identifier();
+
+ boolean bEncoding = false;
+ boolean bStandalone = false;
+
+ if (VERSION.equals(s)) {
+ fVersion = getAttrValue();
+ s = parse_identifier();
+ } else {
+ err(pos, ERR_VERSION_EXPECTED);
+ }
+
+ if (ENCODING.equals(s)) {
+ fEncoding = getAttrValue().toUpperCase();
+ s = parse_identifier();
+ bEncoding = true;
+ }
+
+ if (STANDALONE.equals(s)) {
+ fStandalone = getAttrValue();
+ s = parse_identifier();
+ bStandalone = true;
+ }
+
+ if (s != null) {
+ if (bEncoding && bStandalone)
+ err(ERR_CLOSE_TAG3_EXPECTED);
+
+ if (!bEncoding && !bStandalone)
+ err(ERR_ENCODING_STANDALONE_EXPECTED);
+
+ if (bEncoding)
+ err(ERR_STANDALONE_EXPECTED);
+ err(ERR_CLOSE_TAG3_EXPECTED);
+ }
+
+ clearWhiteSpaces();
+ pos = fPos;
+ if (!accept_seq("?>"))
+ err(pos, ERR_CLOSE_TAG3_EXPECTED);
+ return true;
+ }
+
+ char prevCh = 0;
+
+ while (true) {
+ if (currentChar == '>') {
+ if (prevCh == '?') {
+ accept_char('>');
+ clearWhiteSpaces();
+
+ return true;
+ }
+ err(ERR_QUESTIONMARK_EXPECTED);
+ } else if (currentChar == '<') {
+ err(ERR_ILLEGAL_CHARACTER + " ('<')");
+ }
+ prevCh = currentChar;
+ getNextChar();
+ }
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parses a comment. The grammar is:<br>
+ * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'<br>
+ * Note that the grammar does not allow a comment ending in --->. The
+ * following example is not well-formed.<br>
+ * <code>
+ * <!-- B+, B, or B---></code>
+ *
+ * @param aParent
+ * The parent tag
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ */
+ protected boolean parse_comment(TagClass aParent) throws IOException {
+ if (accept_char('-')) {
+ if (!accept_char('-')) {
+ err(ERR_DASH_EXPECTED);
+ }
+
+ while (true) {
+ if (accept_char('-')) {
+ if (accept_char('-')) {
+ if (accept_char('>')) {
+ break;
+ }
+
+ err(ERR_GT_EXPECTED);
+ }
+ }
+
+ if (!getNextChar()) {
+ err(ERR_COMMENT_CLOSE_EXPECTED);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parses either normal or special tag
+ *
+ * @param aParent
+ * The parent tag that the successfully parsed tag will (if it is
+ * normal tag or CDATA element) be added
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_cahr
+ * @see parse_tag_normal
+ * @see parse_tag_special
+ * @see clearWhiteSpaces
+ */
+ protected boolean parse_tag(TagClass aParent) throws IOException {
+ clearWhiteSpaces();
+ try {
+ fCurrentLevel++;
+
+ if (accept_char('<')) {
+ if (parse_tag_normal(aParent) || parse_tag_special(aParent)) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ fCurrentLevel--;
+ }
+ }
+
+ /**
+ * Parses the content of the tag (including sub-tags and sub-elements)
+ *
+ * @param aParent
+ * The parent tag that the content and tags will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see parse_PCDATA
+ * @see parse_tag
+ */
+ protected boolean parse_content(TagClass aParent) throws IOException {
+ return (parse_PCDATA(aParent) || parse_tag(aParent));
+ }
+
+ /**
+ * Parses a CDATA tag (or CDATA content element).
+ *
+ * @param aParent
+ * The parent tag that the content will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean parse_tag_CDATA(TagClass aParent) throws IOException {
+ if (accept_char('[')) {
+ int pos = fPos;
+ if (!accept_seq(CDATA))
+ err(pos, ERR_CDATA_EXPECTED);
+
+ if (!accept_char('['))
+ err(ERR_OPENSQBRACKET_EXPECTED);
+
+ do {
+ if (currentChar != '>') {
+ aParent.getContentBuffer().append(currentChar);
+ } else {
+ CharBuffer sb = aParent.getContentBuffer();
+ int l = sb.length();
+
+ if (l >= 2) {
+ if (sb.charAt(l - 1) == ']' && sb.charAt(l - 2) == ']') {
+ sb.setLength(l - 2); // Truncates the extra "]]"
+ // symbols appended at the
+ // end
+
+ getNextChar();
+ return true;
+ }
+ }
+ sb.append(currentChar);
+ }
+ } while (getNextChar());
+
+ err(fPos - 1, ERR_CLOSE_CDATA_EXPECTED);
+ }
+ return false;
+ }
+
+ /**
+ * Parses PCDATA content (Parseable Content DATA). The EntityRefs and
+ * CharRefs that are parsed will be turned to its symbol equivalent.
+ *
+ * @param aParent
+ * The parent tag that the PCDATA will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ * @see parse_CharRef
+ * @see parse_EntityRef
+ */
+ protected boolean parse_PCDATA(TagClass aParent) throws IOException {
+ boolean result = false;
+ while (currentChar != '<') {
+ result = true;
+
+ CharBuffer sbContent = aParent.getContentBuffer();
+
+ if (accept_char('&')) {
+ int pos = fPos;
+
+ if (!parse_CharRef(sbContent))
+ if (!parse_EntityRef(sbContent, false))
+ err(pos - 1, ERR_ENTITY_EXPECTED);
+
+ } else {
+ sbContent.append(currentChar);
+
+ if (!getNextChar())
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Accepts one character from the input stream and if it's successful moves
+ * one character forward.
+ *
+ * @param ch
+ * The character that should be accepted
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean accept_char(char ch) throws IOException {
+ if (currentChar == ch) {
+ getNextChar();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Accepts a sequence of characters given by seq parameter. If the sequence
+ * is accepted successfully then the currentChar field will contain the
+ * character immediately after the accepted sequence.
+ *
+ * @param seq
+ * The character sequence that should be accepted
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean accept_seq(String seq) throws IOException {
+ for (int i = 0; i < seq.length(); i++) {
+ if (!accept_char(seq.charAt(i)))
+ return false;
+ }
+ return true;
+ }
+
+ private static final String[] fEntities = (fOldBehaviour) ? fOld_entities : fNew_entities;
+ private static final char[] fEnt_chars = (fOldBehaviour) ? fOld_ent_chars : fNew_ent_chars;
+
+ /**
+ * <code>
+ * EntityRef ::= '&' + EntityValue + ';'<br>
+ * EntityValue ::= 'amp' | 'quot' | 'apos' | 'gt' | 'lt' | identifier
+ * </code>
+ *
+ * @param sb
+ * The string buffer that the recognized entity will be appended
+ * to
+ * @throws IOException
+ * @return true on success and false otherwise
+ * @see parse_identifier
+ * @see accept_char
+ */
+ protected boolean parse_EntityRef(CharBuffer sb, boolean inAttribute) throws IOException {
+ String ent = parse_identifier();
+
+ if (!accept_char(';')) {
+ err(ERR_SEMICOLON_EXPECTED);
+ }
+
+ if (!inAttribute) {
+ int length = fEntities.length;
+ for (int i = 0; i < length; i++) {
+ if (fEntities[i] == ent) { // 'ent' is interned by
+ // parse_identifier() function
+ sb.append(fEnt_chars[i]);
+ return true;
+ }
+ }
+ }
+
+ sb.append('&');
+ if (ent != null) {
+ sb.append(ent);
+ }
+ sb.append(';');
+
+ return true;
+ }
+
+ /**
+ * Parses a CharReference and if it is successful then appends it to the
+ * passed CharBuffer
+ *
+ * @param sb
+ * CharBuffer that the parsed CharReference will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean parse_CharRef(CharBuffer sb) throws IOException {
+ if (accept_char('#')) {
+ // TODO - Postponed...
+ while (currentChar != ';') {
+ getNextChar();
+ }
+
+ if (!accept_char(';')) {
+ err(fPos - 1, ERR_SEMICOLON_EXPECTED);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clears the white spaces starting from the current position
+ *
+ * @throws IOException
+ */
+ protected void clearWhiteSpaces() throws IOException {
+ while (Character.isWhitespace(currentChar)) {
+ if (!getNextChar())
+ break;
+ }
+ }
+
+ /**
+ * Throws an IOException with a given message. The current line number and
+ * line position are appended to the error message
+ *
+ * @param message
+ * The message of the exception
+ * @throws IOException
+ */
+ protected void err(String message) throws IOException {
+ err(fPos, message);
+ }
+
+ /**
+ * Throws an IOException with the given message for the given line position.
+ * The current line number and position (pos) are appended to the exception
+ * message
+ *
+ * @param pos
+ * The line position that the error will be reported for
+ * @param message
+ * @throws IOException
+ */
+ protected void err(int pos, String message) throws IOException {
+ throw new IOException("[Line: " + fLine + ", Pos: " + pos + "] " + message);
+ }
+
+ /**
+ * Initiates parsing of the XML file given through aInputStream or aReader
+ * in the given constructor when creating XMLReader object.
+ *
+ * @throws IOException
+ * if an error occurs during reading the XML file or if a
+ * parsing error eccurs.
+ */
+ protected void parseXML() throws IOException {
+ TagClass rootTag = new TagClass();
+
+ try {
+ getNextChar();
+ clearWhiteSpaces();
+
+ boolean start = false;
+
+ while (accept_char('<')) {
+ start = true;
+ int pos = fPos;
+
+ if (fPos == 2 && fLine == 1) {
+ if (parse_xml_prolog(rootTag)) {
+ // System.out.println("XML Prolog found.");
+ // System.out.println("XML Version: " + fVersion + ",
+ // encoding: " + fEncoding);
+ setEncoding(fEncoding);
+ clearWhiteSpaces();
+ continue;
+ }
+ } else {
+ setEncoding(fDefaultEncoding);
+ }
+
+ if (!parse_tag_special(rootTag)) {
+ if (parse_tag_normal(rootTag)) {
+ // TODO da se proveri dali e dostignat kraja na file-a,
+ // ako ne e -
+ // togava ot tuk natatuk moje da ima samo komentari.
+ return;
+ }
+ err(pos, ERR_TAGNAME2_EXPECTED);
+ }
+
+ clearWhiteSpaces();
+ }
+
+ if (!start) {
+ err(ERR_LT_EXPECTED);
+ }
+ } catch (IOException ioe) {
+ if (fDebug) {
+ ioe.printStackTrace(System.err);
+ }
+
+ throw ioe;
+ }
+ }
+
+ /**
+ * Parses a XML file given through aInputStream and during the parsing
+ * notifies aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag event
+ * @param aLevel
+ * see parseXML(Reader aReader, TagListener aListener, int aLevel
+ * description
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(InputStream aInputStream, TagListener aListener, int aLevel) throws IOException {
+ XMLReader xml = new XMLReader(aInputStream, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+
+ /**
+ * Parses a XML file given through aReader and during the parsing notifies
+ * aListener for close-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * a reader that will be used to read the XML file from
+ * @param aListener
+ * TagListener that will be notified on close-tag event
+ * @param aLevel
+ * indicates the tag level that the listener will be invoked for.
+ * For example if the XML is:<br>
+ *
+ * <pre>
+ * <a>
+ * <b>
+ * <c />
+ * </b>
+ * </a>
+ * </pre>
+ *
+ * <br>
+ * and the passed aLevel is 2 then the listener will be invoked
+ * only for tags that have level 2, i.e. in our example the
+ * listener will be invoked only for tag <b><br>
+ * <ul>
+ * <li>Value less than 0 indicates "invoke listener for all
+ * tags no matter what are their levels"</li>
+ * <li>Value of 0 indicates that the listener must not be
+ * invoked in general no matter what is the tag level</li>
+ * <li>Value greater than 0 indicates the tag level that the
+ * listener will be invoked for</li>
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(Reader aReader, TagListener aListener, int aLevel) throws IOException {
+ XMLReader xml = new XMLReader(aReader, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+
+ /**
+ * Returns the XML version attribute
+ *
+ * @return the XML file version attribute
+ */
+ public String getVersion() {
+ return fVersion;
+ }
+
+ /**
+ * Returns the XML encoding attribute
+ *
+ * @return the XML encoding attribute
+ */
+ public String getEncoding() {
+ return fEncoding;
+ }
+
+ /**
+ * Returns the value of XML standalone attribute
+ *
+ * @return the value of XML standalone attribute
+ */
+ public String getStandalone() {
+ return fStandalone;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLUtil.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLUtil.java
new file mode 100644
index 0000000..96f188f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XMLUtil.java
@@ -0,0 +1,363 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.util.Vector;
+
+import org.eclipse.equinox.internal.util.string.CharBuffer;
+
+/**
+ * Class with utility methods for extracting attributes from xml tags
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class XMLUtil {
+
+ /**
+ * Coded chars placed in the xml content.
+ */
+ public static String[] coded = {"&", " ", "&crlf;", "&tab;", "<", ">", """, "'"};
+
+ /**
+ * Corresponding decoded chars. First five of them cannot exist in plain
+ * (decoded) form in the xml content, but the rest can be eighter coded or
+ * decoded
+ */
+ public static String[] decoded = {"&", " ", "\n", "\t", "<", ">", "\"", "'"};
+
+ /**
+ * Replaces specified strings in a source string with corresponding strings
+ *
+ * @param str
+ * source string
+ * @param toBeReplaced
+ * array of strings which will be replaced with the next
+ * parameter
+ * @param replaceWith
+ * array of the strings corresponding to "toBeReplaced"
+ * parameter. the length should be equal
+ * @param length
+ * specify how many parameters of the string array will be
+ * replaced, the rest of "toBeReplaced" parameter will not.
+ * @return replaced string
+ */
+ public static String replaceChars(String str, String[] toBeReplaced, String[] replaceWith, int length) {
+
+ /*
+ * first 5 characters are not allowed to exists in such form in xml, the
+ * rest remains the same
+ */
+ CharBuffer strBuf;
+ for (int i = 0; i < length; i++) {
+ strBuf = new CharBuffer();
+ replace(str, toBeReplaced[i], replaceWith[i], strBuf);
+ str = strBuf.toString();
+ }
+ return str;
+ }
+
+ /**
+ * Replaces a specified string in a source string with a corresponding
+ * string and adds the result in a buffer.
+ *
+ * @param src
+ * source string
+ * @param toBeReplaced
+ * string which will be replaced with the next parameter
+ * @param toReplaceWith
+ * the string corresponding to "toBeReplaced" parameter.
+ * @param strBuf
+ * here is added the replaced source string.
+ */
+ public static void replace(String src, String toBeReplaced, String toReplaceWith, CharBuffer strBuf) {
+
+ int j;
+ int pos = 0;
+ int length = toBeReplaced.length();
+ while (((j = src.indexOf(toBeReplaced, pos)) > -1)) {
+ strBuf.append(src.substring(pos, j));
+ strBuf.append(toReplaceWith);
+ pos = j + length;
+ }
+ strBuf.append(src.substring(pos));
+ }
+
+ /**
+ * Converts special xml chars to valid string e.g. "&lt;" becomes "<"
+ * and "&gt;" becomes ">"
+ *
+ * @param src
+ * source string which is coded
+ * @return decoded string
+ */
+ public static String getDecoded(String src) {
+ int begin = src.indexOf('&');
+ if (begin == -1) {
+ /*
+ * improve speed when there is not any coded string (each coded
+ * string begins with '&')
+ */
+ return src;
+ }
+ int end = src.indexOf(';', begin);
+ if (end == -1) {
+ /*
+ * improve speed when there is not any coded string (each coded
+ * string ends with ';')
+ */
+ return src;
+ }
+ int lastBegin = 0;
+ CharBuffer strBuf = new CharBuffer();
+ while (begin != -1) {
+ end = src.indexOf(';', begin); /*
+ * this is used on the second turn
+ * of the cycle
+ */
+ if (end == -1) {
+ strBuf.append(src.substring(begin));
+ return strBuf.toString();
+ }
+ strBuf.append(src.substring(lastBegin, begin));
+ String part = src.substring(begin, end + 1);
+ boolean found = false;
+ for (int i = 0; i < decoded.length; i++) {
+ if (part.equals(coded[i])) {
+ strBuf.append(decoded[i]);
+ begin += part.length();
+ lastBegin = begin;
+ begin = src.indexOf('&', lastBegin);
+ found = true;
+ break; /*
+ * one word replaced, go on searching with rest of
+ * the src
+ */
+ }
+ }
+ if (!found) {
+ strBuf.append(part);
+ begin += part.length();
+ lastBegin = begin;
+ begin = src.indexOf('&', begin);
+ }
+ }
+ strBuf.append(src.substring(lastBegin));
+ return strBuf.toString().intern();
+ }
+
+ /**
+ * Extracts the name of the tag, from string containing the name and
+ * attributes
+ *
+ * @param tagAndAttributes
+ * string containing the name and attributes
+ * @return tag name string separated by space from the tag attributes
+ * @deprecated use TagClass.getName() instead
+ */
+ public static String getTagName(String tagAndAttributes) {
+ int nameIndex = tagAndAttributes.indexOf(' ');
+ if (nameIndex != -1) {
+ int tabIndex = tagAndAttributes.indexOf('\t');
+ if (tabIndex > -1 && tabIndex < nameIndex) {
+ return tagAndAttributes.substring(0, tabIndex);
+ }
+ return tagAndAttributes.substring(0, nameIndex);
+ }
+
+ nameIndex = tagAndAttributes.indexOf('/');
+ if (nameIndex != -1) {
+ return tagAndAttributes.substring(0, nameIndex);
+ }
+ return tagAndAttributes;
+ }
+
+ public static boolean isEmptyTag(String tagName) {
+ return '/' == tagName.charAt(tagName.length() - 1);
+ }
+
+ /**
+ * Checks whether the tag name string between < and >, ends with "/".
+ *
+ * @param tagName
+ * string between < and > chars
+ * @return true if and only if string ends with "/"
+ */
+
+ protected static final String[] f_entities = {"amp", "nbsp", "crlf", "tab", "lt", "gt", "quot", "apos"};
+ protected static final char[] f_ent_chars = {'&', ' ', '\n', '\t', '<', '>', '"', '\''};
+
+ private static boolean substituteEntity(String ent, CharBuffer cb) {
+ for (int j = 0; j < f_entities.length; j++) {
+ if (f_entities[j].equals(ent)) {
+ cb.append(f_ent_chars[j]);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extracts an attribute value for a specified attribute name from tag
+ * string.
+ *
+ * @param tag
+ * whole tag name
+ * @param att
+ * attribute name
+ * @return value of the attribute if exists otherwise return null
+ * @deprecated use TagClass.getAttrList() and TagClass.getAttribute(String
+ * attr) instead
+ */
+ public static String getAttributeValue(String tag, String att, int begin) {
+ if (att.length() == 0)
+ return null;
+
+ int start = 0;
+ while (start >= 0) {
+ start = tag.indexOf(att, start);
+
+ if (start >= 0) {
+ // Checks if leading and trailing chars are valid attr_name
+ // chars
+ boolean b1 = (start > 0) ? XMLReader.isNameChar(tag.charAt(start - 1)) : true;
+ boolean b2 = ((start + att.length() + 1) < tag.length()) ? XMLReader.isNameChar(tag.charAt(start + att.length())) : true;
+
+ start += att.length();
+ if (!b1 && !b2) {
+ start = tag.indexOf('=', start) + 1;
+ if (start <= 0)
+ return null;
+
+ while (start < tag.length() && Character.isWhitespace(tag.charAt(start))) {
+ start++;
+ }
+
+ char quot = tag.charAt(start);
+ int end = tag.indexOf(quot, start + 1);
+ if ((start != -1) && (end > start)) {
+ int pos = tag.indexOf('&', start + 1);
+ if (pos < 0 || pos > end)
+ return tag.substring(start + 1, end).intern();
+
+ CharBuffer cb = new CharBuffer();
+
+ char ch;
+ for (int i = (start + 1); i < end; i++) {
+ ch = tag.charAt(i);
+ if (ch == '&') {
+ pos = tag.indexOf(';', i);
+ String ent = tag.substring(i + 1, pos);
+
+ if (substituteEntity(ent, cb)) {
+ i = pos;
+ continue;
+ }
+
+ cb.append('&');
+ cb.append(ent);
+ cb.append(';');
+ i = pos;
+ continue;
+ }
+ cb.append(ch);
+ }
+
+ return cb.toString().intern();
+ }
+ start = end;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Extracts attribute names from tag string.
+ *
+ * @param tag
+ * whole tag name
+ * @return A vector with all attribute names
+ * @deprecated use TagClass.getAttrList() instead
+ */
+ public static Vector getAttributeNames(String tag) {
+ Vector names = new Vector();
+ int start = tag.indexOf(' '); /* avoid name of this tag */
+ int end;
+ while (start != -1) {
+ end = tag.indexOf('=', start + 1);
+ if (end == -1) {
+ break;
+ }
+ names.addElement(tag.substring(start + 1, end));
+ start = tag.indexOf('"', end + 1);
+ if (start == -1) {
+ break;
+ }
+ start = tag.indexOf('"', start + 1);
+ if (start == -1) {
+ break;
+ }
+ start = tag.indexOf(' ', start + 1);
+ }
+ return names;
+ }
+
+ /**
+ * Extracts attribute values from tag string.
+ *
+ * @param tag
+ * whole tag name
+ * @return A vector with all attribute values
+ * @deprecated use TagClass.getAttrList() instead
+ */
+ public static Vector getAttributeValues(String tag) {
+ Vector values = new Vector();
+ int start = tag.indexOf('=');
+ int end;
+ while (start != -1) {
+ start = tag.indexOf('"', start + 1);
+ end = tag.indexOf('"', start + 1);
+ if ((start == -1) || (end == -1)) {
+ break;
+ }
+ values.addElement(tag.substring(start + 1, end).intern());
+ start = tag.indexOf('=', end + 1);
+ }
+ return values;
+ }
+
+ /**
+ * Returns content of the specified subtag.
+ *
+ * @param tag
+ * parent tag
+ * @param pos
+ * position of the searched tag in child array of the parent tag
+ * @param name
+ * name of the searched tag
+ * @return content of the specified subtag
+ * @exception IllegalArgumentException
+ * if the specified name does not match the name of the tag
+ * at specified position
+ * @deprecated use TagClass.getContent(int pos, String name) instead
+ */
+
+ public static String getContent(TagClass tag, int pos, String name) throws IllegalArgumentException {
+ TagClass subTag = tag.getTagAt(pos);
+ if (subTag.getName().equals(name)) {
+ return subTag.getContent();
+ }
+ throw new IllegalArgumentException("Missing subtag " + name + " in " + tag.getName());
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XmlSerializer.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XmlSerializer.java
new file mode 100644
index 0000000..80a9fff
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/XmlSerializer.java
@@ -0,0 +1,444 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml;
+
+import java.io.*;
+import java.util.Vector;
+
+/**
+ * XmlSerializer is an utility class, that simplifies creation of XML files. It
+ * is designed to be similar to the XML Pull API serializer.
+ *
+ * @author Valentin Valchev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class XmlSerializer {
+
+ private Writer writer; // underlying writer
+ private Vector stack; // of XML entity names
+ private StringWriter attrsWriter; // current attribute writer
+ private StringBuffer attrs; // current attribute string
+ private boolean empty; // is the current node empty
+ private boolean closed; // is the current node closed...
+ private String indentString;
+ private boolean prologWritten;
+
+ /**
+ * Opens and initializes XML output on top of existing Writer.
+ *
+ * @param writer
+ * the output writer
+ * @return self
+ */
+ public XmlSerializer setOutput(Writer writer) {
+ this.writer = writer;
+ this.closed = true;
+ this.prologWritten = false;
+ if (stack == null) {
+ stack = new Vector(5);
+ } else {
+ stack.removeAllElements();
+ }
+ if (attrs != null) {
+ attrs.setLength(0);
+ }
+ return this;
+ }
+
+ /**
+ * Set to use binary output stream with given encoding.
+ *
+ * @param os
+ * the output stream
+ * @param encoding
+ * the output encoding
+ * @throws IOException
+ * @see #setOutput(Writer)
+ * @throws NullPointerException
+ * is output stream is null
+ */
+ public void setOutput(OutputStream os, String encoding) throws IOException {
+ if (os == null)
+ throw new NullPointerException("output stream can not be null");
+ Writer writer = encoding != null ? //
+ new OutputStreamWriter(os, encoding)
+ : //
+ new OutputStreamWriter(os);
+ setOutput(writer);
+ }
+
+ /**
+ * Writes XML prolog code:
+ *
+ * <pre>
+ * <?xml version="1.0"?>
+ * </pre>
+ *
+ * @param encoding
+ * the selected encoding. If <code>null</code> encoding
+ * attribute is not written.
+ * @param standalone
+ * if the XML is stand-alone, no DTD or schema
+ * @return self
+ * @throws IOException
+ * on I/O error
+ * @throws IllegalStateException
+ * if prolog code is already written
+ */
+ public XmlSerializer startDocument(String encoding, Boolean standalone) throws IOException {
+ if (prologWritten) {
+ throw new IllegalStateException("Prolog already written");
+ }
+ writer.write("<?xml version=\"1.0\"");
+ if (encoding != null) {
+ writer.write(" encoding=\"");
+ writer.write(encoding);
+ writer.write("\"");
+ }
+ if (standalone != null) {
+ writer.write(" standalone=\"");
+ writer.write(standalone.booleanValue() ? "yes" : "no");
+ writer.write("\"");
+ }
+ writer.write("?>");
+ prologWritten = true;
+ return this;
+ }
+
+ /**
+ * Close this writer. It does not close the underlying writer, but does
+ * throw an exception if there are as yet unclosed tags.
+ *
+ * @throws IllegalStateException
+ * if tags are not closed
+ */
+ public void endDocument() {
+ if (!stack.isEmpty()) {
+ throw new IllegalStateException("Tags are not all closed. Possibly, '" + pop() + "' is unclosed. ");
+ }
+ }
+
+ /**
+ * Returns the current depth of the element. Outside the root element, the
+ * depth is 0. The depth is incremented by 1 when startTag() is called. The
+ * depth is decremented after the call to endTag() event was observed.
+ *
+ * <pre>
+ * <!-- outside --> 0
+ * <root> 1
+ * sometext 1
+ * <foobar> 2
+ * </foobar> 2
+ * </root> 1
+ * <!-- outside --> 0
+ * </pre>
+ *
+ * @return the current level
+ */
+ public int getDepth() {
+ return stack.size();
+ }
+
+ /**
+ * Begin to output an entity.
+ *
+ * @param name
+ * name of entity.
+ * @return self
+ * @throws IOException
+ * on I/O error
+ */
+ public XmlSerializer startTag(String name) throws IOException {
+ if (!prologWritten) {
+ startDocument(null, null);
+ }
+ closeOpeningTag();
+ closed = false;
+ indent();
+ writer.write("<");
+ writer.write(name);
+ stack.addElement(name);
+ empty = true;
+ return this;
+ }
+
+ /**
+ * End the current entity silently without validating if correct tag is
+ * closed.
+ *
+ * @return self
+ * @throws IOException
+ * @deprecated see {@link #endTag(String)}
+ */
+ public XmlSerializer endTag() throws IOException {
+ return endTag(null);
+ }
+
+ /**
+ * End the current entity. This will throw an exception if it is called when
+ * there is not a currently open entity.
+ *
+ * @param aName
+ * tag to close. This is used mostly for validation
+ * @return self
+ * @throws IOException
+ * on I/O error
+ * @throws IllegalStateException
+ * if no entity waits to be closed
+ * @throws IllegalArgumentException
+ * if expected element name is not the same
+ */
+ public XmlSerializer endTag(String aName) throws IOException {
+
+ if (stack.isEmpty()) {
+ throw new IllegalStateException("Called endEntity too many times.");
+ }
+ String name = pop();
+ if (name != null) {
+ if (aName != null && !aName.equals(name)) {
+ throw new IllegalArgumentException("Expected element name '" + name + "', not '" + aName + "'");
+ }
+ if (empty) {
+ writeAttributes();
+ writer.write("/>");
+ } else {
+ indent();
+ writer.write("</");
+ writer.write(name);
+ writer.write(">");
+ }
+ empty = false;
+ }
+ return this;
+ }
+
+ /**
+ * Write an attribute out for the current entity. Any XML characters in the
+ * value are escaped. Calls to attribute() MUST follow a call to startTag()
+ * immediately.
+ *
+ * @param localName
+ * name of attribute.
+ * @param value
+ * value of attribute.
+ * @return self
+ * @throws IOException
+ * on I/O error
+ * @throws IllegalStateException
+ * if opening tag is closed
+ */
+ public XmlSerializer attribute(String localName, String value) throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Opening tag is already closed");
+ }
+ if (localName == null) {
+ throw new IllegalArgumentException("Attribute name is null");
+ }
+ if (value == null)
+ value = "";
+ if (attrsWriter == null) {
+ attrsWriter = new StringWriter();
+ attrs = attrsWriter.getBuffer();
+ }
+ attrs.append(" ");
+ attrs.append(localName);
+ attrs.append("=\"");
+ writeEscape(value, attrsWriter);
+ attrs.append("\"");
+ return this;
+ }
+
+ /**
+ * Output body text. Any XML characters are escaped.
+ *
+ * @param text
+ * the body text
+ * @return self
+ * @throws IOException
+ * on I/O error
+ */
+ public XmlSerializer text(String text) throws IOException {
+ closeOpeningTag();
+ if (text != null && text.length() > 0) {
+ indent();
+ empty = false;
+ writeEscape(text, writer);
+ }
+ return this;
+ }
+
+ /**
+ * Writes raw, CDATA section
+ *
+ * @param text
+ * the data
+ * @return self
+ * @throws IOException
+ * on I/O error
+ */
+ public XmlSerializer cdsect(String text) throws IOException {
+ closeOpeningTag();
+ writer.write("<![CDATA[");
+ writer.write(text);
+ writer.write("]]>");
+ return this;
+ }
+
+ /**
+ * Writes XML comment code
+ *
+ * @param text
+ * the comment
+ * @return self
+ * @throws IOException
+ * on I/O error
+ */
+ public XmlSerializer comment(String text) throws IOException {
+ closeOpeningTag();
+ writer.write("<!--");
+ writer.write(text);
+ writer.write("-->");
+ return this;
+ }
+
+ /**
+ * Writes DOCTYPE declaration. The document must be open prior calling that
+ * method.
+ *
+ * @param text
+ * declaration
+ * @return self
+ * @throws IOException
+ * on I/O error
+ * @throws IllegalStateException
+ * if document is not open or start tag is already started
+ */
+ public XmlSerializer docdecl(String text) throws IOException {
+ if (!prologWritten) {
+ throw new IllegalStateException("Document is not open");
+ }
+ if (getDepth() != 0) {
+ throw new IllegalStateException("Cannot add DOCTYPE after start tag has been opened!");
+ }
+ closeOpeningTag();
+ writer.write("<!DOCTYPE");
+ writer.write(text);
+ writer.write(">");
+ return this;
+ }
+
+ /**
+ * Enables/disables indentation
+ *
+ * @param indent
+ * an indentation string or <code>null</code> to disable
+ * indentation.
+ */
+ public void setIndent(boolean indent) {
+ this.indentString = indent ? "\t" : null;
+ }
+
+ // close off the opening tag
+ private void closeOpeningTag() throws IOException {
+ if (!this.closed) {
+ writeAttributes();
+ closed = true;
+ writer.write(">");
+ }
+ }
+
+ // write out all current attributes
+ private void writeAttributes() throws IOException {
+ if (attrs != null) {
+ writer.write(attrs.toString());
+ attrs.setLength(0);
+ empty = false;
+ }
+ }
+
+ private final String pop() {
+ int location = stack.size() - 1;
+ String ret = (String) stack.elementAt(location);
+ stack.removeElementAt(location);
+ return ret;
+ }
+
+ private final void indent() throws IOException {
+ if (indentString != null) {
+ writer.write('\n');
+ for (int i = 0; i < stack.size(); i++) {
+ writer.write(indentString);
+ }
+ }
+ }
+
+ private final void writeEscape(String text, Writer out) throws IOException {
+ // escape '<', '&', '>', ', "
+ char[] buf = text.toCharArray();
+ int len = buf.length;
+ int pos = 0;
+ for (int i = 0; i < len; i++) {
+ final char ch = buf[i];
+
+ if (ch == '&') {
+ if (i > pos)
+ out.write(buf, pos, i - pos);
+ out.write("&");
+ pos = i + 1;
+ } else if (ch == '<') {
+ if (i > pos)
+ out.write(buf, pos, i - pos);
+ out.write("<");
+ pos = i + 1;
+ } else if (ch == '>') {
+ if (i > pos)
+ out.write(buf, pos, i - pos);
+ out.write(">");
+ pos = i + 1;
+ } else if (ch == '"') {
+ if (i > pos)
+ out.write(buf, pos, i - pos);
+ out.write("&guot;");
+ pos = i + 1;
+ } else if (ch == '\'') {
+ if (i > pos)
+ out.write(buf, pos, i - pos);
+ out.write("'");
+ pos = i + 1;
+ } else if (ch < 32) {
+ // in XML 1.0 only legal character are #x9 | #xA | #xD
+ if (ch == 9 || ch == 10 || ch == 13) {
+ // pass through
+ } else {
+ throw new IllegalStateException("character " + ch + " (" + Integer.toString(ch) + ") is not allowed in output");
+ }
+ }
+ }
+ if (len > pos) {
+ out.write(buf, pos, len - pos);
+ }
+ }
+
+ /*
+ * public static void main(String[] args) throws IOException { XmlSerializer
+ * o = new XmlSerializer(); Writer x = new java.io.StringWriter();
+ * o.setIndent(true); o.setOutput(x); o.startDocument("UTF-8", null)//
+ * .startTag("test")// .startTag("inner")// .attribute("attribute",
+ * "value").text("text & ' < > \\\" ;[")// .comment("this is an embedded
+ * comment inside the inner body")// .endTag("inner")//
+ * .startTag("entry").cdsect("testing [] > < & ;").endTag("entry")//
+ * .endTag("test")// .endDocument(); System.out.println(x); }
+ */
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/TagImpl.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/TagImpl.java
new file mode 100644
index 0000000..2ecd239
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/TagImpl.java
@@ -0,0 +1,381 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml.impl;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.equinox.internal.util.string.CharBuffer;
+import org.eclipse.equinox.internal.util.xml.Tag;
+
+/**
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class TagImpl implements Tag {
+
+ private CharBuffer fContent = null;
+ private String fContentString = null;
+ private String fName = null;
+ private Hashtable fAttributes = null;
+ private Vector fTags = null;
+ private int fLine = -1;
+ protected boolean fInline = false;
+
+ /**
+ * A hashtable enumerator class for empty hash tables, specializes the
+ * general Enumerator
+ */
+ private static Enumeration fEmptyEnumeration = new Enumeration() {
+ public boolean hasMoreElements() {
+ return false;
+ }
+
+ public Object nextElement() {
+ throw new NoSuchElementException("Hashtable Enumerator");
+ }
+ };
+
+ /**
+ *
+ */
+ public TagImpl() {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getAttribute(java.lang.String)
+ */
+ public String getAttribute(String aAttrName) {
+ return (String) ((fAttributes != null) ? fAttributes.get(aAttrName) : null);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getAttributes()
+ */
+ public Enumeration getAttributeNames() {
+ return (fAttributes != null) ? fAttributes.keys() : fEmptyEnumeration;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getAttributeValues()
+ */
+ public Enumeration getAttributeValues() {
+ return (fAttributes != null) ? fAttributes.elements() : fEmptyEnumeration;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getName()
+ */
+ public String getName() {
+ return fName;
+ }
+
+ /**
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getContent() note: The content that
+ * is to be returned will be trimmed first
+ */
+ public String getContent() {
+ if (fContentString != null) {
+ return fContentString;
+ } else if (fContent != null) {
+ fContentString = fContent.trim();
+ fContent = null;
+ return fContentString;
+ }
+ return "";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getLine()
+ */
+ public int getLine() {
+ return fLine;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#size()
+ */
+ public int size() {
+ return (fTags != null) ? fTags.size() : 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getTagAt(int)
+ */
+ public Tag getTagAt(int aPosition) {
+ return (Tag) fTags.elementAt(aPosition);
+ }
+
+ protected void setName(String aName) {
+ fName = aName;
+ }
+
+ protected void addAttribute(String aAttrName, String aAttrValue) {
+ if (fAttributes == null) {
+ fAttributes = new Hashtable(1);
+ }
+ fAttributes.put(aAttrName, aAttrValue);
+ }
+
+ protected void addTag(Tag aTag) {
+ if (fTags == null) {
+ fTags = new Vector(2, 3);
+ }
+ fTags.addElement(aTag);
+ }
+
+ protected void appendContent(CharBuffer toAppend) {
+ if (fContent == null) {
+ fContent = new CharBuffer(toAppend.length());
+ }
+ fContent.append(toAppend.getValue());
+ }
+
+ protected void appendContent(String toAppend) {
+ if (fContent == null) {
+ fContent = new CharBuffer(toAppend.length());
+ }
+ fContent.append(toAppend);
+ }
+
+ protected CharBuffer getContentBuffer() {
+ if (fContent == null) {
+ fContent = new CharBuffer(5);
+ }
+ return fContent;
+ }
+
+ protected void setLine(int aLine) {
+ fLine = aLine;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.equinox.internal.util.xml.Tag#getContent(int, java.lang.String)
+ */
+ public String getContent(int aPos, String aName) {
+ Tag child = getTagAt(aPos);
+ if (child == null)
+ throw new NullPointerException("There is no such a tag. [Parent tag name] = [" + aName + "], [child tag index] = " + aPos + ", [child tag name] = [" + aName + ']');
+
+ if (child.getName().equals(aName))
+ return child.getContent();
+
+ throw new IllegalArgumentException("There is no such a tag. [Parent tag name] = [" + aName + "], [child tag index] = " + aPos + ", [child tag name] = [" + aName + ']');
+ }
+
+ /* begin serialization see FR [ 13325 ] XML: Make Tag externalizable */
+
+ /**
+ * @see org.eclipse.equinox.internal.util.io.Externalizable#readObject(java.io.InputStream)
+ */
+ public void readObject(InputStream is) throws Exception {
+ if (!(is instanceof ObjectInput)) {
+ is = new ObjectInputStream(is);
+ }
+ readExternal((ObjectInput) is);
+ }
+
+ /**
+ * @see org.eclipse.equinox.internal.util.io.Externalizable#writeObject(java.io.OutputStream)
+ */
+ public void writeObject(OutputStream os) throws Exception {
+ if (!(os instanceof ObjectOutput)) {
+ os = new ObjectOutputStream(os);
+ }
+ writeExternal((ObjectOutput) os);
+ os.flush();
+ }
+
+ /**
+ * @param input
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
+ fName = input.readUTF();
+ /* read content */
+ boolean hasContent = input.readBoolean();
+ if (hasContent) {
+ fContentString = input.readUTF();
+ }
+ int size;
+ /* read attributes */
+ size = input.readInt();
+ if (size > 0) {
+ fAttributes = new Hashtable(size, 1);
+ for (int i = 0; i < size; i++) {
+ String key = input.readUTF();
+ String val = input.readUTF();
+ fAttributes.put(key, val);
+ }
+ }
+ /* read elements */
+ size = input.readInt();
+ if (size > 0) {
+ fTags = new Vector(size);
+ for (int i = 0; i < size; i++) {
+ TagImpl tag = new TagImpl();
+ tag.readExternal(input);
+ fTags.addElement(tag);
+ }
+ }
+ }
+
+ /**
+ * @param output
+ * @throws IOException
+ */
+ public void writeExternal(ObjectOutput output) throws IOException {
+ output.writeUTF(fName);
+ /* write content */
+ String content = null;
+ if (fContentString != null) {
+ content = fContentString;
+ } else if (fContent != null) {
+ content = fContent.trim();
+ }
+ if (content != null) {
+ output.writeBoolean(true);
+ output.writeUTF(content);
+ } else {
+ output.writeBoolean(false);
+ }
+ /* write attributes */
+ if (fAttributes != null) {
+ output.writeInt(fAttributes.size());
+ for (Enumeration e = fAttributes.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String val = (String) fAttributes.get(key);
+ output.writeUTF(key);
+ output.writeUTF(val);
+ }
+ } else {
+ output.writeInt(0);
+ }
+ /* write elements */
+ if (fTags != null) {
+ output.writeInt(fTags.size());
+ for (int i = 0, size = fTags.size(); i < size; i++) {
+ TagImpl current = (TagImpl) fTags.elementAt(i);
+ current.writeExternal(output);
+ }
+ } else {
+ output.writeInt(0);
+ }
+ output.flush();
+ }
+
+ /* begin simple, utility method for debugging */
+
+ /**
+ * Writes the tag as formatted XML into the given writer.
+ *
+ * @param out
+ * the output writer, where tag is serialized to XML
+ * @param level
+ * the initial level. If set to negative value, the indent
+ * /formatting/ of the XML is disable, and it is written on a
+ * single line. Otherwise, the level is used to calculate the
+ * offset from the left margin.
+ * @throws IOException
+ * on I/O error
+ */
+ public void writeXml(Writer out, int level) throws IOException {
+ boolean indent = level >= 0;
+ for (int i = 0; indent && i < level; i++)
+ out.write(' '); /* indent */
+ out.write('<');
+ out.write(fName);
+ if (fAttributes != null) {
+ for (Enumeration e = fAttributes.keys(); e.hasMoreElements();) {
+ out.write(' ');
+ String key = (String) e.nextElement();
+ String val = (String) fAttributes.get(key);
+ out.write(key);
+ out.write("=\"");
+ out.write(val);
+ out.write('"');
+ }
+ }
+ /* don't use getContent() - this will demolish the buffer */
+ String content = null;
+ if (fContentString != null) {
+ content = fContentString;
+ } else if (fContent != null) {
+ content = fContent.trim();
+ }
+ boolean isShort = content == null && fTags == null;
+ if (isShort) {
+ /* close immediately */
+ out.write("/>");
+ if (indent)
+ out.write('\n');
+ } else {
+ out.write('>');
+ if (indent)
+ out.write('\n');
+ /* write elements */
+ for (int i = 0; fTags != null && i < fTags.size(); i++) {
+ TagImpl child = (TagImpl) fTags.elementAt(i);
+ child.writeXml(out, level < 0 ? -1 : (level + 1));
+ }
+ /* write contents */
+ if (content != null) {
+ for (int i = 0; indent && i < level + 1; i++)
+ out.write(' '); /* indent */
+ out.write(content);
+ if (indent)
+ out.write('\n');
+ }
+ /* write closing tag */
+ for (int i = 0; indent && i < level; i++)
+ out.write(' '); /* indent */
+ out.write("</");
+ out.write(fName);
+ out.write('>');
+ if (indent)
+ out.write('\n');
+ }
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ StringWriter out = new StringWriter();
+ try {
+ writeXml(out, 0);
+ } catch (IOException e) {
+ /* this will never happen */
+ }
+ return out.toString();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/XMLParserImpl.java b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/XMLParserImpl.java
new file mode 100644
index 0000000..a4947c3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/impl/XMLParserImpl.java
@@ -0,0 +1,1219 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.util.xml.impl;
+
+import java.io.*;
+import org.eclipse.equinox.internal.ds.Activator;
+import org.eclipse.equinox.internal.util.string.CharBuffer;
+import org.eclipse.equinox.internal.util.xml.ExTagListener;
+
+/**
+ * <p>
+ * Class used for reading of xml files, creating tree structure of 'TagImpl' for
+ * each xml tag. When reader reaches a closed tag it notifies a given
+ * 'TagListener' and sends the last tag to it. If closing tag does not
+ * correspond with the last open IllegalArgumentException is thrown. There is a
+ * debug property 'xml.debug' used to dump an Exceptions occurred while
+ * operation is running.
+ * </p>
+ *
+ * <p>
+ * The parser, in general, is a simple XML parser that implements
+ * "Recursive descent" parsing method.
+ *
+ * Known limitations:<br>
+ *
+ * <pre>
+ * Currently this XMLParser does not support the following special tags:
+ * 1. <?TAG_NAME ..... ?> or also "Processing Instructions"
+ * 2. <!DOCTYPE .... >
+ * 3. <!ELEMENT .... >
+ * 4. <!ATTLIST .... >
+ * 5. <!ENTITY .... >
+ * </pre>
+ *
+ * <br>
+ * The parser skips these tags (it searches for '>' symbol and closes the
+ * 'special' tag).<br>
+ *
+ * @author Ivan Dimitrov
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class XMLParserImpl {
+
+ private static final String DEBUG = "xml.debug";
+ private static final String INTERN_ATTRIBUTES = "xml.intern.attributes";
+
+ private static final String CDATA = "CDATA";
+ private static final String XML = "xml";
+ private static final String VERSION = "version";
+ private static final String ENCODING = "encoding";
+ private static final String STANDALONE = "standalone";
+
+ private static final String ERR_EOS = "End-of-stream reached before the end of XML.";
+ private static final String ERR_ENTITY_EXPECTED = "Entity reference or Character reference expected.";
+ private static final String ERR_EQUAL_EXPECTED = "'=' expected.";
+ private static final String ERR_QUOT_EXPECTED = "''' or '\"' expected.";
+ private static final String ERR_GT_EXPECTED = "'>' expected.";
+ private static final String ERR_LT_EXPECTED = "'<' expected.";
+ private static final String ERR_CLOSE_TAG1_EXPECTED = "'/' or tag name expected.";
+ private static final String ERR_CLOSE_TAG2_EXPECTED = "'>', '/>' or more attributes expected.";
+ private static final String ERR_CLOSE_TAG3_EXPECTED = "'?>' expected.";
+ private static final String ERR_CONTENT_EXPECTED = "Content data, new tag or closing tag expected.";
+ private static final String ERR_QUESTIONMARK_EXPECTED = "'?' expected.";
+ private static final String ERR_ILLEGAL_CHARACTER = "Illegal character.";
+ private static final String ERR_TAGNAME_EXPECTED = "Tag name expected.";
+ private static final String ERR_TAGNAME2_EXPECTED = "Tag name, '?' or '!' expected.";
+ private static final String ERR_DASH_EXPECTED = "'-' expected.";
+ private static final String ERR_COMMENT_CLOSE_EXPECTED = "'-->' expected.";
+ private static final String ERR_CDATA_EXPECTED = "'CDATA' expected.";
+ private static final String ERR_OPENSQBRACKET_EXPECTED = "'[' expected.";
+ private static final String ERR_CLOSE_CDATA_EXPECTED = "']]>' expected.";
+ private static final String ERR_SEMICOLON_EXPECTED = "';' expected.";
+ private static final String ERR_XMLPROLOG_EXPECTED = "XML prolog '<?xml' is not expected at this position.";
+ private static final String ERR_VERSION_EXPECTED = "'version' attribute expected.";
+ private static final String ERR_ENCODING_STANDALONE_EXPECTED = "'encoding', 'standalone' or '?>' expected.";
+ private static final String ERR_STANDALONE_EXPECTED = "'standalone' attribute expected.";
+
+ protected static final boolean fDebug = Activator.getBoolean(DEBUG);
+ protected static final boolean fInternAttributes = Activator.getBoolean(INTERN_ATTRIBUTES);
+
+ private String fDefaultEncoding = "UTF-8";
+
+ // private CharBuffer c;
+ private CharBuffer temp = new CharBuffer();
+ private CharBuffer temp2 = null;
+
+ protected Reader fReader = null;
+ protected InputStream fStream = null;
+
+ protected char currentChar = 0;
+ protected ExTagListener fExTagListener;
+
+ protected int fLine = 1;
+ protected int fPos = 0;
+
+ protected int fLevel = -1;
+ protected int fCurrentLevel = 1;
+
+ private String fVersion = "1.0";
+ private String fEncoding = "UTF-8";
+ private String fStandalone = "yes";
+
+ /**
+ * An empty default constructor
+ *
+ */
+ public XMLParserImpl() {
+ //
+ }
+
+ /**
+ * Constructs a new XMLReader. <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on tag-open or tag-close
+ * events
+ * @throws IOException
+ */
+ public XMLParserImpl(InputStream aInputStream, ExTagListener aListener) {
+ fStream = aInputStream;
+ fExTagListener = aListener;
+ }
+
+ /**
+ * Constructs a new XMLReader. <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * a Reader to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on tag-open or tag-close
+ * events
+ */
+ public XMLParserImpl(Reader aReader, ExTagListener aListener) {
+ fReader = aReader;
+ fExTagListener = aListener;
+ }
+
+ /**
+ * Sets the level of tags bellow which the listener will be notified for.
+ * For internal use only.
+ *
+ * @param aLevel
+ */
+ public void setLevel(int aLevel) {
+ fLevel = aLevel;
+ }
+
+ /**
+ * Sets the parser's encoding. If there is a current encoding associated
+ * with the parser the method returns immediately
+ *
+ * @param aEncoding
+ * new encoding to be set
+ * @throws UnsupportedEncodingException
+ * if the encoding is not supported.
+ */
+ protected void setEncoding(String aEncoding) {
+ if (fReader == null) {
+ try {
+ fReader = new InputStreamReader(fStream, aEncoding);
+ } catch (Exception e) {
+ if (fDebug) {
+ System.err.println("[XMLParserImpl] Failed setting the encoding \"" + aEncoding + "\", continue parsing with the default one.");
+ }
+ fReader = new InputStreamReader(fStream);
+ }
+ }
+ }
+
+ /* A helper function to reuse a temp CharBuffers without recreating it */
+ protected CharBuffer getCharBuffer() {
+ if (temp.length() <= 0) {
+ return temp;
+ } else if (temp2 == null) {
+ temp2 = new CharBuffer(0);
+ return temp2;
+ } else if (temp2.length() <= 0) {
+ return temp2;
+ }
+ return new CharBuffer(0);
+ }
+
+ protected char prev_char = 0;
+ private char[] fBuffer = new char[4096];
+ private int fBufferLen = 0;
+ private int fBufferPos = 0;
+
+ /**
+ * Reads the next char from the input stream and sets it to private field
+ * <code>currentChar</code>
+ *
+ * @return true if the next char is successfully read of false if
+ * End-Of-Stream is reached
+ * @throws IOException
+ * if some error occurs during reading the character or if the
+ * caller tries to read beyond the End-Of-Stream.
+ */
+ protected boolean getNextChar() throws IOException {
+ // // Reading characters without buffering
+ // int ichar = 0;
+ // int count = 0;
+ // while (ichar == 0 && count < 100) {
+ // ichar = fReader.read();
+ // count++;
+ // }
+ //
+ // if (ichar == 0)
+ // throw new IOException("Failed to read from the input file.");
+ //
+ // if (ichar < 0 && prev_char == 0)
+ // throw new IOException(ERR_EOS);
+ //
+ // char ch = (char) ichar;
+
+ char ch;
+
+ if (fReader == null) { // If there is no associated reader,
+ int ach = fStream.read(); // then reads from the InputStream until
+ if (ach < 0) { // the rigth encoding is recognized
+ ach = 0;
+ }
+
+ ch = (char) ach;
+ if (ch == 0 && prev_char == 0) {
+ throw new IOException(ERR_EOS);
+ }
+ } else {
+ // Buffered reading
+ if (fBufferLen < 0) {
+ throw new IOException(ERR_EOS);
+ }
+
+ if ((fBufferPos) >= fBufferLen) {
+ // Refetch the buffer
+ fBufferLen = 0;
+ fBufferPos = 0;
+ int count = 0;
+ while (fBufferLen == 0 && count < 100) {
+ fBufferLen = fReader.read(fBuffer);
+ count++;
+ }
+
+ ch = (fBufferLen > 0) ? fBuffer[fBufferPos++] : 0;
+
+ if (fBufferLen == 0) {
+ fBufferLen = -1;
+ }
+ } else {
+ ch = fBuffer[fBufferPos++];
+ }
+ }
+
+ prev_char = currentChar;
+ currentChar = ch;
+ fPos++;
+
+ switch (ch) {
+ case '\n' :
+ if (prev_char != '\r') {
+ fLine++;
+ }
+ fPos = 0;
+ break;
+ case '\r' :
+ fPos = 0;
+ fLine++;
+ break;
+ }
+ return (currentChar != 0);
+ }
+
+ /**
+ * Parses the attribute value and if it's successful then adds it to the
+ * CharBuffer. If there are EntityReferences of CharReferences in the
+ * attribute value, they will be turned to their equivalent symbols.<br>
+ * attr_value ::= (acceptable_char | EntityRef | CharRef)* - quot_symbol
+ *
+ * @see parse_attr
+ * @see parse_CharRef
+ * @see parse_EntityRef
+ */
+ protected void parse_attr_value(CharBuffer sb, char quot) throws IOException {
+ while (currentChar != quot && currentChar != '<') {
+ if (accept_char('&')) {
+ if (!parse_CharRef(sb)) {
+ if (!parse_EntityRef(sb)) {
+ err(fPos - 1, ERR_ENTITY_EXPECTED);
+ }
+ }
+ } else {
+ sb.append(currentChar);
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute with the given simplified grammar:<br>
+ *
+ * <pre>
+ * attribute ::= S* + attr_name + S* + '=' + S* + ('\'' + (attr_value - '\'') + '\'')) | ('"' + (attr_value - '"') + '"'))
+ * attr_value ::= (acceptable_char | EntityRef | CharRef)*
+ * attr_name ::= identifier
+ * </pre>
+ *
+ * @param aParent
+ * the parent tag where the correctly parsed attribute will be
+ * added
+ * @throws IOException
+ * @see parse_identifier
+ * @see parse_attr_value
+ */
+ protected boolean parse_attr(TagImpl aParent) throws IOException {
+ clearWhiteSpaces();
+
+ String attrName = parse_identifier();
+
+ if (attrName != null) {
+ clearWhiteSpaces();
+
+ if (!accept_char('=')) {
+ err(ERR_EQUAL_EXPECTED);
+ }
+
+ clearWhiteSpaces();
+
+ char quot = 0;
+ if (accept_char('"')) {
+ quot = '"';
+ } else if (accept_char('\'')) {
+ quot = '\'';
+ } else {
+ err(ERR_QUOT_EXPECTED);
+ }
+
+ CharBuffer sb = getCharBuffer();
+ parse_attr_value(sb, quot);
+
+ if (!accept_char(quot)) {
+ err("'" + quot + "' expected.");
+ }
+
+ String attrValue = sb.toString();
+
+ if (fInternAttributes) {
+ attrValue.intern();
+ }
+
+ aParent.addAttribute(attrName, attrValue);
+ sb.setLength(0);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parses a tag attribute list with the following simplified grammar:
+ *
+ * <pre>
+ * attr_list ::= attribute*
+ * @param aParent the parent tag that the parsed attributes will be added to
+ * @return true if at least one attribute is parsed correctly and false otherwise
+ * @throws IOException
+ * @see parse_attr
+ *
+ */
+ protected boolean parse_attr_list(TagImpl aParent) throws IOException {
+ boolean result = false;
+ while (parse_attr(aParent)) {
+ result = true;
+ }
+ return result;
+ }
+
+ private static final char bA = 'A' - 1;
+ private static final char aZ = 'Z' + 1;
+ private static final char ba = 'a' - 1;
+ private static final char az = 'z' + 1;
+ private static final char b0 = '0' - 1;
+ private static final char a9 = '9' + 1;
+
+ /**
+ * This method returns true is the passed character may be used as starting
+ * character for tag name and attribute name
+ *
+ * @param ch
+ * the tested character
+ * @return true if the character could be used as starting character for a
+ * tag name and an attribute name and false otherwise
+ */
+ protected final boolean isNameStartChar(char ch) {
+ return (ch > bA && ch < aZ) || (ch > ba && ch < az) || (ch == ':') || (ch == '_') || (ch > 0xBF && ch < 0xD7) || (ch > 0xD7 && ch < 0xF7) || (ch > 0xF7 && ch < 0x300) || (ch > 0x36F && ch < 0x37E) || (ch > 0x37E && ch < 0x2000) || (ch > 0x200B && ch < 0x200E) || (ch > 0x206F && ch < 0x2190) || (ch > 0x2BFF && ch < 0x2FF0) || (ch > 0x3000 && ch < 0xD800) || (ch > 0xF900 && ch < 0xFDD0) || (ch > 0xFDEF && ch < 0xFFFE) || (ch > 0x0FFFF && ch < 0xF0000);
+ }
+
+ /**
+ * This method returns true if the passed characted may be used as part of a
+ * tag name or an attribute name
+ *
+ * @param ch
+ * the tested character
+ * @return true is the characted could be used as part of a tag name or an
+ * attribute name and false otherwise
+ */
+ protected final boolean isNameChar(char ch) {
+ return (ch == '-') || (ch == '.') || (ch == 0xB7) || (ch > b0 && ch < a9) || isNameStartChar(ch) || (ch > 0x02FF && ch < 0x0370) || (ch > 0x203E && ch < 0x2041);
+ }
+
+ /**
+ * Parses an identifier.
+ *
+ * @return an identifier if it is parsed successfully and null otherwise
+ * @throws IOException
+ */
+ protected String parse_identifier() throws IOException {
+ if (isNameStartChar(currentChar)) {
+ CharBuffer sb = getCharBuffer();
+
+ while (isNameChar(currentChar)) {
+ sb.append(currentChar);
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ String result = sb.toString().intern();
+ sb.setLength(0);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ * Parses a tag name and if it is successfully parsed the method sets it as
+ * a name of the parent tag
+ *
+ * @param aParent
+ * parent tag
+ * @return true if the name is parsed successfully and false otherwise
+ * @throws IOException
+ * @see parse_identifier
+ */
+ protected boolean parse_tag_name(TagImpl aParent) throws IOException {
+ String name = parse_identifier();
+ if (name != null) {
+ aParent.setName(name);
+ }
+ return name != null;
+ }
+
+ /**
+ * Helper function that notify listeners depending on certain conditions
+ * such as if the tag event is on-close or on-open
+ *
+ * @param aTag
+ * The tag that the notification event is valid for.
+ * @param isStart
+ * true if the event is on-open and false if it is on-close
+ */
+ protected void notifyListeners(TagImpl aTag, boolean isStart) {
+ try {
+ if (isStart) {
+ fExTagListener.startTag(aTag);
+ } else {
+ fExTagListener.endTag(aTag);
+ }
+ } catch (RuntimeException re) {
+ if (fDebug) {
+ System.err.println("An outside exception occured while processing a tag on line " + aTag.getLine() + ", the tag name is: " + aTag.getName());
+ re.printStackTrace(System.err);
+ }
+ throw re;
+ }
+ }
+
+ /**
+ * Parses a normal tag. There are two cases - (1) the tag has separate open
+ * and close tag elements and (2) the tag is simple suchas <tag_name ...
+ * />
+ *
+ * @param aParent
+ * The parent tag that this tag will be added to if the parsing
+ * is successful
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see clearWhiteSpaces
+ * @see parse_tag_name
+ * @see parse_attr_list
+ * @see notifyListeners
+ * @see accept_char
+ * @see accept_seq
+ * @see parse_PCDATA
+ */
+ protected boolean parse_tag_normal(TagImpl aParent) throws IOException {
+ // Looking for a tag_name (identifier)
+ if (isNameStartChar(currentChar)) {
+ TagImpl tag = new TagImpl();
+ tag.setLine(fLine);
+
+ parse_tag_name(tag);
+ parse_attr_list(tag);
+
+ clearWhiteSpaces();
+
+ if (accept_char('/')) {
+ if (!accept_char('>')) {
+ err(ERR_GT_EXPECTED);
+ }
+ aParent.addTag(tag);
+
+ if ((fLevel <= 0 || fLevel == fCurrentLevel)) {
+ notifyListeners(tag, true);
+ notifyListeners(tag, false);
+ }
+
+ return true;
+ } else if (accept_char('>')) {
+
+ notifyListeners(tag, true);
+
+ while (true) {
+ clearWhiteSpaces();
+ int pos = fPos;
+ if (currentChar == '<') { // Normal tag, Special tag or
+ // closing tag
+ if (!parse_tag(tag)) { // It may be a special tag.
+ if (!accept_char('/')) {
+ err(pos + 1, ERR_CLOSE_TAG1_EXPECTED);
+ }
+
+ // trying to accept: tag_name + S* + '>'
+ pos = fPos;
+ if (!accept_seq(tag.getName())) {
+ err(pos, '\'' + tag.getName() + "' string expected.");
+ }
+
+ clearWhiteSpaces();
+ if (!accept_char('>')) {
+ err(ERR_GT_EXPECTED);
+ }
+
+ aParent.addTag(tag);
+
+ if (fLevel <= 0 || fLevel == fCurrentLevel) {
+ notifyListeners(tag, false);
+ }
+
+ return true;
+ }
+ } else {
+ if (!parse_PCDATA(tag)) {
+ break;
+ }
+ }
+ }
+ err(ERR_CONTENT_EXPECTED);
+ } else {
+ err(ERR_CLOSE_TAG2_EXPECTED);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the specified attribute is already parsed and false
+ * otherwise
+ *
+ * @param aTag
+ * the which attribute will be examined.
+ * @param attrName
+ * an attribute name
+ * @return true if the specified attribute is already parsed and false
+ * otherwise
+ */
+ protected boolean accept_attr(TagImpl aTag, String attrName) {
+ return aTag.getAttribute(attrName) != null;
+ }
+
+ /**
+ * Parses the XML prolog tag, i.e.<br>
+ * <code> <?xml version="..." encoding="..." standalone="..." ?> </code><br>
+ *
+ * @param parent
+ * the parent tag (in this case this is the root "fake" tag,
+ * which the listeners will never be informed for...)
+ * @throws IOException
+ * if an exception occurs during read operations from the Reader
+ * or the InputStream
+ */
+ protected boolean parse_xml_prolog(TagImpl parent) throws IOException {
+ if (accept_char('?')) {
+ TagImpl tag = new TagImpl();
+
+ if (parse_tag_name(tag)) {
+ if (tag.getName().equalsIgnoreCase(XML)) {
+ clearWhiteSpaces();
+
+ int pos = fPos;
+
+ if (parse_attr(tag)) {
+ String s = tag.getAttribute(VERSION);
+
+ if (s == null) {
+ err(pos, ERR_VERSION_EXPECTED);
+ }
+
+ fVersion = s;
+
+ clearWhiteSpaces();
+ pos = fPos;
+ if (parse_attr(tag)) {
+ clearWhiteSpaces();
+
+ if (accept_attr(tag, ENCODING)) {
+ fEncoding = tag.getAttribute(ENCODING);
+
+ pos = fPos;
+ if (parse_attr(tag)) {
+ clearWhiteSpaces();
+ if (accept_attr(tag, STANDALONE)) {
+ fStandalone = tag.getAttribute(STANDALONE);
+ } else {
+ err(pos, ERR_STANDALONE_EXPECTED);
+ }
+ }
+ } else if (accept_attr(tag, STANDALONE)) {
+ fStandalone = tag.getAttribute(STANDALONE);
+ } else {
+ err(pos, ERR_ENCODING_STANDALONE_EXPECTED);
+ }
+ }
+
+ clearWhiteSpaces();
+ pos = fPos;
+ if (!accept_seq("?>")) {
+ err(pos, ERR_CLOSE_TAG3_EXPECTED);
+ }
+ } else {
+ err(pos, ERR_VERSION_EXPECTED);
+ }
+ clearWhiteSpaces();
+ return true;
+ }
+
+ char prevCh = 0;
+
+ while (true) {
+ if (currentChar == '>') {
+ if (prevCh == '?') {
+ accept_char('>');
+ clearWhiteSpaces();
+
+ return true;
+ }
+ err(ERR_QUESTIONMARK_EXPECTED);
+ } else if (currentChar == '<') {
+ err(ERR_ILLEGAL_CHARACTER + " ('<')");
+ }
+ prevCh = currentChar;
+ getNextChar();
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parses special tags, such that begins with:<br>
+ *
+ * <pre><code>
+ * <!-- comments
+ * <!tag_name Parsing instructions
+ * <![ CDATA element
+ * <? DOCTYPE, etc.
+ * </code></pre>
+ *
+ * @param aParent
+ * The parent tag that this tag will be added to if the parsing
+ * is successful
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ * @see clearWhiteSpaces
+ * @see parse_tag_CDATA
+ * @see parse_tag_name
+ * @see parse_comment
+ */
+ protected boolean parse_tag_special(TagImpl aParent) throws IOException {
+ if (accept_char('!')) {
+
+ TagImpl tag = new TagImpl();
+
+ if (parse_tag_name(tag)) {
+ clearWhiteSpaces();
+
+ while (true) {
+ if (accept_char('>')) {
+ clearWhiteSpaces();
+ return true;
+ }
+ getNextChar();
+ }
+ } else if (parse_tag_CDATA(aParent)) { // parse CDATA tag
+ return true;
+ } else if (parse_comment(tag)) {
+ return true;
+ }
+ } else if (accept_char('?')) {
+ TagImpl tag = new TagImpl();
+
+ int pos = fPos;
+ if (parse_tag_name(tag)) {
+ if (tag.getName().equals(XML)) {
+ err(pos - 2, ERR_XMLPROLOG_EXPECTED);
+ }
+
+ char prevCh = 0;
+ while (true) {
+ if (currentChar == '>') {
+ if (prevCh == '?') {
+ accept_char('>');
+ clearWhiteSpaces();
+ return true;
+ }
+ }
+ prevCh = currentChar;
+ getNextChar();
+ }
+
+ } else {
+ err(pos, ERR_TAGNAME_EXPECTED);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parses a comment. The grammar is:<br>
+ * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'<br>
+ * Note that the grammar does not allow a comment ending in --->. The
+ * following example is not well-formed.<br>
+ * <code>
+ * <!-- B+, B, or B---></code>
+ *
+ * @param aParent
+ * The parent tag
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ */
+ protected boolean parse_comment(TagImpl aParent) throws IOException {
+ if (accept_char('-')) {
+ if (!accept_char('-')) {
+ err(ERR_DASH_EXPECTED);
+ }
+
+ while (true) {
+ if (accept_char('-')) {
+ if (accept_char('-')) {
+ if (accept_char('>')) {
+ break;
+ }
+ err(ERR_GT_EXPECTED);
+ }
+ }
+
+ if (!getNextChar()) {
+ err(ERR_COMMENT_CLOSE_EXPECTED);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parses either normal or special tag
+ *
+ * @param aParent
+ * The parent tag that the successfully parsed tag will (if it is
+ * normal tag or CDATA element) be added
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_cahr
+ * @see parse_tag_normal
+ * @see parse_tag_special
+ * @see clearWhiteSpaces
+ */
+ protected boolean parse_tag(TagImpl aParent) throws IOException {
+ clearWhiteSpaces();
+ try {
+ fCurrentLevel++;
+
+ if (accept_char('<')) {
+ if (parse_tag_normal(aParent) || parse_tag_special(aParent)) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ fCurrentLevel--;
+ }
+ }
+
+ /**
+ * Parses the content of the tag (including sub-tags and sub-elements)
+ *
+ * @param aParent
+ * The parent tag that the content and tags will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see parse_PCDATA
+ * @see parse_tag
+ */
+ protected boolean parse_content(TagImpl aParent) throws IOException {
+ return (parse_PCDATA(aParent) || parse_tag(aParent));
+ }
+
+ /**
+ * Parses a CDATA tag (or CDATA content element).
+ *
+ * @param aParent
+ * The parent tag that the content will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean parse_tag_CDATA(TagImpl aParent) throws IOException {
+ if (accept_char('[')) {
+ int pos = fPos;
+
+ if (!accept_seq(CDATA)) {
+ err(pos, ERR_CDATA_EXPECTED);
+ }
+
+ if (!accept_char('[')) {
+ err(ERR_OPENSQBRACKET_EXPECTED);
+ }
+
+ do {
+ if (currentChar != '>') {
+ aParent.getContentBuffer().append(currentChar);
+ } else {
+ CharBuffer sb = aParent.getContentBuffer();
+ int l = sb.length();
+
+ if (l >= 2) {
+ if (sb.charAt(l - 1) == ']' && sb.charAt(l - 2) == ']') {
+ sb.setLength(l - 2); // Truncates the extra "]]"
+ // symbols appended at the
+ // end
+
+ getNextChar();
+ return true;
+ }
+ }
+ sb.append(currentChar);
+ }
+ } while (getNextChar());
+
+ err(fPos - 1, ERR_CLOSE_CDATA_EXPECTED);
+ }
+ return false;
+ }
+
+ /**
+ * Parses PCDATA content (Parseable Content DATA). The EntityRefs and
+ * CharRefs that are parsed will be turned to its symbol equivalent.
+ *
+ * @param aParent
+ * The parent tag that the PCDATA will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ * @see accept_char
+ * @see parse_CharRef
+ * @see parse_EntityRef
+ */
+ protected boolean parse_PCDATA(TagImpl aParent) throws IOException {
+ boolean result = false;
+ while (currentChar != '<') {
+ result = true;
+
+ CharBuffer sbContent = aParent.getContentBuffer();
+
+ if (accept_char('&')) {
+ int pos = fPos;
+ if (!parse_CharRef(sbContent)) {
+ if (!parse_EntityRef(sbContent)) {
+ err(pos - 1, ERR_ENTITY_EXPECTED);
+ }
+ }
+ } else {
+ sbContent.append(currentChar);
+
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Accepts one character from the input stream and if it's successful moves
+ * one character forward.
+ *
+ * @param ch
+ * The character that should be accepted
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean accept_char(char ch) throws IOException {
+ if (currentChar == ch) {
+ getNextChar();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Accepts a sequence of characters given by seq parameter. If the sequence
+ * is accepted successfully then the currentChar field will contain the
+ * character immediately after the accepted sequence.
+ *
+ * @param seq
+ * The character sequence that should be accepted
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean accept_seq(String seq) throws IOException {
+ for (int i = 0; i < seq.length(); i++) {
+ if (!accept_char(seq.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected static final String[] entities = {"amp", "apos", "lt", "gt", "quot"};
+ protected static final char[] ent_chars = {'&', '\'', '<', '>', '"'};
+
+ /**
+ * <code>
+ * EntityRef ::= '&' + EntityValue + ';'<br>
+ * EntityValue ::= 'amp' | 'quot' | 'apos' | 'gt' | 'lt' | identifier
+ * </code>
+ *
+ * @param sb
+ * The string buffer that the recognized entity will be appended
+ * to
+ * @throws IOException
+ * @return true on success and false otherwise
+ * @see parse_identifier
+ * @see accept_char
+ */
+ protected boolean parse_EntityRef(CharBuffer sb) throws IOException {
+ String ent = parse_identifier();
+
+ if (!accept_char(';')) {
+ err(ERR_SEMICOLON_EXPECTED);
+ }
+
+ int length = entities.length;
+ for (int i = 0; i < length; i++) {
+ if (entities[i] == ent) { // 'ent' is interned by
+ // parse_identifier() function
+ sb.append(ent_chars[i]);
+ return true;
+ }
+ }
+
+ sb.append('&');
+
+ if (ent != null && ent.length() > 0) {
+ sb.append(ent);
+ }
+
+ sb.append(';');
+
+ return true;
+ }
+
+ /**
+ * Parses a CharReference and if it is successful then appends it to the
+ * passed CharBuffer
+ *
+ * @param sb
+ * CharBuffer that the parsed CharReference will be added to
+ * @return true on success and false otherwise
+ * @throws IOException
+ */
+ protected boolean parse_CharRef(CharBuffer sb) throws IOException {
+ if (accept_char('#')) {
+ // TODO - Postponed...
+ while (currentChar != ';') {
+ getNextChar();
+ }
+
+ if (!accept_char(';')) {
+ err(fPos - 1, ERR_SEMICOLON_EXPECTED);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clears the white spaces starting from the current position
+ *
+ * @throws IOException
+ */
+ protected void clearWhiteSpaces() throws IOException {
+ while (Character.isWhitespace(currentChar)) {
+ if (!getNextChar()) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Throws an IOException with a given message. The current line number and
+ * line position are appended to the error message
+ *
+ * @param message
+ * The message of the exception
+ * @throws IOException
+ */
+ protected void err(String message) throws IOException {
+ err(fPos, message);
+ }
+
+ /**
+ * Throws an IOException with the given message for the given line position.
+ * The current line number and position (pos) are appended to the exception
+ * message
+ *
+ * @param pos
+ * The line position that the error will be reported for
+ * @param message
+ * @throws IOException
+ */
+ protected void err(int pos, String message) throws IOException {
+ throw new IOException("[Line: " + fLine + ", Pos: " + pos + "] " + message);
+ }
+
+ /**
+ * Initiates parsing of the XML file given through aInputStream or aReader
+ * in the given constructor when creating XMLReader object.
+ *
+ * @throws IOException
+ * if an error occurs during reading the XML file or if a
+ * parsing error eccurs.
+ */
+ public void parseXML() throws IOException {
+ TagImpl rootTag = new TagImpl();
+
+ try {
+ getNextChar();
+ clearWhiteSpaces();
+
+ boolean start = false;
+
+ while (accept_char('<')) {
+ start = true;
+ int pos = fPos;
+
+ if (fPos == 2 && fLine == 1) {
+ if (parse_xml_prolog(rootTag)) {
+ // System.out.println("XML Prolog found.");
+ // System.out.println("XML Version: " + fVersion + ",
+ // encoding: " + fEncoding);
+ setEncoding(fEncoding);
+ clearWhiteSpaces();
+ continue;
+ }
+ } else {
+ setEncoding(fDefaultEncoding);
+ }
+
+ if (!parse_tag_special(rootTag)) {
+ if (parse_tag_normal(rootTag)) {
+ // TODO da se proveri dali e dostignat kraja na file-a,
+ // ako ne e - togava ot tuk natatuk moje da ima samo
+ // komentari.
+ return;
+ }
+ err(pos, ERR_TAGNAME2_EXPECTED);
+ }
+
+ clearWhiteSpaces();
+ }
+
+ if (!start) {
+ err(ERR_LT_EXPECTED);
+ }
+ } catch (IOException ioe) {
+ if (fDebug) {
+ ioe.printStackTrace(System.err);
+ }
+
+ throw ioe;
+ }
+ }
+
+ /**
+ * Parses a XML file given through aInputStream and during the parsing
+ * notifies aListener for close-tag and open-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aInputStream
+ * an InputStream to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on close-tag and open-tag
+ * events
+ * @param aLevel
+ * indicates the tag level that the listener will be invoked for.
+ * For example if the XML is:<br>
+ *
+ * <pre>
+ * <a>
+ * <b>
+ * <c />
+ * </b>
+ * </a>
+ * </pre>
+ *
+ * <br>
+ * and the passed aLevel is 2 then the listener will be invoked
+ * only for tags that have level 2, i.e. in our example the
+ * listener will be invoked only for tag <b><br>
+ * <ul>
+ * <li>Value less than 0 indicates "invoke listener for all
+ * tags no matter what are their levels"</li>
+ * <li>Value of 0 indicates that the listener must not be
+ * invoked in general no matter what is the tag level</li>
+ * <li>Value greater than 0 indicates the tag level that the
+ * listener will be invoked for</li>
+ * description
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(InputStream aInputStream, ExTagListener aListener, int aLevel) throws IOException {
+ XMLParserImpl xml = new XMLParserImpl(aInputStream, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+
+ /**
+ * Parses a XML file given through aReader and during the parsing notifies
+ * aListener for close-tag and open-tag events <br>
+ * <br>
+ * <b>Note: The XMLReader does not close the passed Reader or InputStream
+ *
+ * @param aReader
+ * aReader to read the XML file from
+ * @param aListener
+ * ExTagListener that will be notified on close-tag and open-tag
+ * events
+ * @param aLevel
+ * see parseXML(Reader aReader, ExTagListener aListener, int
+ * aLevel description
+ * @throws IOException
+ * if some IO error occurs when reading the XML file or if a
+ * parser error occurs.
+ */
+ public static void parseXML(Reader aReader, ExTagListener aListener, int aLevel) throws IOException {
+ XMLParserImpl xml = new XMLParserImpl(aReader, aListener);
+ xml.setLevel(aLevel);
+ xml.parseXML();
+ }
+
+ /**
+ * Returns the XML version attribute
+ *
+ * @return the XML file version attribute
+ */
+ public String getVersion() {
+ return fVersion;
+ }
+
+ /**
+ * Returns the XML encoding attribute
+ *
+ * @return the XML encoding attribute
+ */
+ public String getEncoding() {
+ return fEncoding;
+ }
+
+ /**
+ * Returns the value of XML standalone attribute
+ *
+ * @return the value of XML standalone attribute
+ */
+ public String getStandalone() {
+ return fStandalone;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/package.html b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/package.html
new file mode 100644
index 0000000..2f76572
--- /dev/null
+++ b/bundles/org.eclipse.equinox.ds/src/org/eclipse/equinox/internal/util/xml/package.html
@@ -0,0 +1,11 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
+<META NAME="Generator" CONTENT="Microsoft Word 97">
+<META NAME="Template" CONTENT="F:\Program Files\Microsoft Office\Office\html.dot">
+</HEAD>
+<BODY LINK="#0000ff" VLINK="#800080">
+
+<P>The <CODE>org.eclipse.equinox.util.xml</CODE> package contains classes that can read and parse a XML document. Through the <CODE>XMLReader</CODE> class you can read a XML from an input stream or through a reader. To get aware of every read tag, you should pass a <CODE>TagListener</CODE> implementation as an argument in the chosen <CODE>read</CODE> method of <CODE>XMLReader</CODE>. When the end of a tag is reached, the <CODE>TagListener</CODE> will received an instance of <CODE>TagClass</CODE> that represents this tag. To read the name, content, attributes and a child tag at a specific index, you can use <CODE>TagClass</CODE> and <CODE>XMLUtil</CODE>. With <CODE>XMLUtil</CODE> you can also replace a tag. You cannot create new XMLs with the XML utilities.</P>
+</BODY>
+</HTML>
diff --git a/bundles/org.eclipse.equinox.io/.classpath b/bundles/org.eclipse.equinox.io/.classpath
new file mode 100644
index 0000000..755d49b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/OSGi%Minimum-1.0"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.io/.cvsignore b/bundles/org.eclipse.equinox.io/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.equinox.io/.project b/bundles/org.eclipse.equinox.io/.project
new file mode 100644
index 0000000..8c9bd86
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.equinox.io</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..d304116
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,331 @@
+#Thu Aug 16 11:00:59 EDT 2007
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=1000
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=false
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false
+org.eclipse.jdt.core.formatter.comment.format_line_comments=false
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=800
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..023a27a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,57 @@
+#Tue Aug 21 11:19:11 CDT 2007
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_core
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=;
+org.eclipse.jdt.ui.ondemandthreshold=3
+org.eclipse.jdt.ui.staticondemandthreshold=99
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.equinox.io/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..c37bbc4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Mar 29 10:30:40 EEST 2007
+eclipse.preferences.version=1
+pluginProject.extensions=false
diff --git a/bundles/org.eclipse.equinox.io/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.io/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4a908f8
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/META-INF/MANIFEST.MF
@@ -0,0 +1,31 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: IO Connector Service
+Bundle-SymbolicName: org.eclipse.equinox.io
+Bundle-Version: 0.1.0.qualifier
+Bundle-Vendor: Eclipse
+Bundle-Activator: org.eclipse.equinox.internal.io.impl.Activator
+Bundle-Description: This bundle contains service for creating connections.
+Import-Package:
+ javax.microedition.io,
+ org.eclipse.equinox.internal.util.hash;version="1.0.0",
+ org.eclipse.equinox.internal.util.pool;version="1.0.0",
+ org.eclipse.equinox.internal.util.ref;version="1.0.0",
+ org.osgi.framework;version="1.0",
+ org.osgi.service.io;version="1.0",
+ org.osgi.service.log;version="1.0.0",
+ org.osgi.util.tracker;version="1.2"
+Export-Package:
+ javax.microedition.io,
+ org.eclipse.equinox.internal.io; x-internal:=true,
+ org.eclipse.equinox.internal.io.impl; x-internal:=true,
+ org.eclipse.equinox.internal.io.util; x-internal:=true
+Bundle-RequiredExecutionEnvironment: OSGi/Minimum-1.0,
+ CDC-1.0/Foundation-1.0,
+ J2SE-1.2,
+ J2SE-1.3,
+ J2SE-1.4,
+ J2SE-1.5,
+ JavaSE-1.6,
+ CDC-1.1/Foundation-1.1
+Provided-Services: class=org.osgi.service.io.ConnectorService
diff --git a/bundles/org.eclipse.equinox.io/OSGI-INF/permissions.perm b/bundles/org.eclipse.equinox.io/OSGI-INF/permissions.perm
new file mode 100644
index 0000000..6cedcdd
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/OSGI-INF/permissions.perm
@@ -0,0 +1,18 @@
+(org.osgi.framework.PackagePermission "javax.microedition.io" "import")
+(org.osgi.framework.PackagePermission "org.osgi.service.io" "import")
+(org.osgi.framework.PackagePermission "org.osgi.service.log" "import")
+(org.osgi.framework.PackagePermission "org.osgi.util.tracker" "import")
+(org.osgi.framework.PackagePermission "org.osgi.framework" "import")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.util.hash" "import")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.util.pool" "import")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.util.ref" "import")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.io" "export")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.io.impl" "export")
+(org.osgi.framework.PackagePermission "org.eclipse.equinox.internal.io.util" "export")
+(org.osgi.framework.PackagePermission "javax.microedition.io" "export")
+(org.osgi.framework.BundlePermission "*" "provide")
+(org.osgi.framework.BundlePermission "*" "host")
+(org.osgi.framework.ServicePermission "org.osgi.service.io.ConnectionFactory" "get")
+(org.osgi.framework.ServicePermission "org.osgi.service.io.ConnectorService" "register")
+(java.util.PropertyPermission "*" "read")
+(java.net.SocketPermission "*" "listen")
diff --git a/bundles/org.eclipse.equinox.io/about.html b/bundles/org.eclipse.equinox.io/about.html
new file mode 100644
index 0000000..40e147d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>January 15, 2008</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/bundles/org.eclipse.equinox.io/build.properties b/bundles/org.eclipse.equinox.io/build.properties
new file mode 100644
index 0000000..9cbab3c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html
+src.includes = about.html
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connection.java
new file mode 100644
index 0000000..1d5fffd
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connection.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface Connection {
+
+ public abstract void close() throws java.io.IOException;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ConnectionNotFoundException.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ConnectionNotFoundException.java
new file mode 100644
index 0000000..68a9016
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ConnectionNotFoundException.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public class ConnectionNotFoundException extends java.io.IOException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ConnectionNotFoundException() {
+ }
+
+ public ConnectionNotFoundException(java.lang.String var0) {
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connector.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connector.java
new file mode 100644
index 0000000..dceb48e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Connector.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public class Connector {
+
+ private Connector() {
+ }
+
+ public static javax.microedition.io.Connection open(java.lang.String var0) throws java.io.IOException {
+ return null;
+ }
+
+ public static javax.microedition.io.Connection open(java.lang.String var0, int var1) throws java.io.IOException {
+ return null;
+ }
+
+ public static javax.microedition.io.Connection open(java.lang.String var0, int var1, boolean var2) throws java.io.IOException {
+ return null;
+ }
+
+ public static java.io.DataInputStream openDataInputStream(java.lang.String var0) throws java.io.IOException {
+ return null;
+ }
+
+ public static java.io.DataOutputStream openDataOutputStream(java.lang.String var0) throws java.io.IOException {
+ return null;
+ }
+
+ public static java.io.InputStream openInputStream(java.lang.String var0) throws java.io.IOException {
+ return null;
+ }
+
+ public static java.io.OutputStream openOutputStream(java.lang.String var0) throws java.io.IOException {
+ return null;
+ }
+
+ public final static int READ = 1;
+
+ public final static int WRITE = 2;
+
+ public final static int READ_WRITE = 3;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ContentConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ContentConnection.java
new file mode 100644
index 0000000..8064b2f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/ContentConnection.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface ContentConnection extends javax.microedition.io.StreamConnection {
+
+ public abstract java.lang.String getEncoding();
+
+ public abstract long getLength();
+
+ public abstract java.lang.String getType();
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Datagram.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Datagram.java
new file mode 100644
index 0000000..b59b5bf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/Datagram.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface Datagram extends java.io.DataInput, java.io.DataOutput {
+
+ public abstract java.lang.String getAddress();
+
+ public abstract byte[] getData();
+
+ public abstract int getLength();
+
+ public abstract int getOffset();
+
+ public abstract void reset();
+
+ public abstract void setAddress(javax.microedition.io.Datagram var0);
+
+ public abstract void setAddress(java.lang.String var0) throws java.io.IOException;
+
+ public abstract void setData(byte[] var0, int var1, int var2);
+
+ public abstract void setLength(int var0);
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/DatagramConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/DatagramConnection.java
new file mode 100644
index 0000000..b97d452
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/DatagramConnection.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface DatagramConnection extends javax.microedition.io.Connection {
+
+ public abstract int getMaximumLength() throws java.io.IOException;
+
+ public abstract int getNominalLength() throws java.io.IOException;
+
+ public abstract javax.microedition.io.Datagram newDatagram(byte[] var0, int var1) throws java.io.IOException;
+
+ public abstract javax.microedition.io.Datagram newDatagram(byte[] var0, int var1, java.lang.String var2) throws java.io.IOException;
+
+ public abstract javax.microedition.io.Datagram newDatagram(int var0) throws java.io.IOException;
+
+ public abstract javax.microedition.io.Datagram newDatagram(int var0, java.lang.String var1) throws java.io.IOException;
+
+ public abstract void receive(javax.microedition.io.Datagram var0) throws java.io.IOException;
+
+ public abstract void send(javax.microedition.io.Datagram var0) throws java.io.IOException;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/HttpConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/HttpConnection.java
new file mode 100644
index 0000000..6a3bb45
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/HttpConnection.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface HttpConnection extends javax.microedition.io.ContentConnection {
+
+ public abstract long getDate() throws java.io.IOException;
+
+ public abstract long getExpiration() throws java.io.IOException;
+
+ public abstract java.lang.String getFile();
+
+ public abstract java.lang.String getHeaderField(int var0) throws java.io.IOException;
+
+ public abstract java.lang.String getHeaderField(java.lang.String var0) throws java.io.IOException;
+
+ public abstract long getHeaderFieldDate(java.lang.String var0, long var1) throws java.io.IOException;
+
+ public abstract int getHeaderFieldInt(java.lang.String var0, int var1) throws java.io.IOException;
+
+ public abstract java.lang.String getHeaderFieldKey(int var0) throws java.io.IOException;
+
+ public abstract java.lang.String getHost();
+
+ public abstract long getLastModified() throws java.io.IOException;
+
+ public abstract int getPort();
+
+ public abstract java.lang.String getProtocol();
+
+ public abstract java.lang.String getQuery();
+
+ public abstract java.lang.String getRef();
+
+ public abstract java.lang.String getRequestMethod();
+
+ public abstract java.lang.String getRequestProperty(java.lang.String var0);
+
+ public abstract int getResponseCode() throws java.io.IOException;
+
+ public abstract java.lang.String getResponseMessage() throws java.io.IOException;
+
+ public abstract java.lang.String getURL();
+
+ public abstract void setRequestMethod(java.lang.String var0) throws java.io.IOException;
+
+ public abstract void setRequestProperty(java.lang.String var0, java.lang.String var1) throws java.io.IOException;
+
+ public final static java.lang.String GET = "GET";
+
+ public final static java.lang.String HEAD = "HEAD";
+
+ public final static java.lang.String POST = "POST";
+
+ public final static int HTTP_ACCEPTED = 202;
+
+ public final static int HTTP_BAD_GATEWAY = 502;
+
+ public final static int HTTP_BAD_METHOD = 405;
+
+ public final static int HTTP_BAD_REQUEST = 400;
+
+ public final static int HTTP_CLIENT_TIMEOUT = 408;
+
+ public final static int HTTP_CONFLICT = 409;
+
+ public final static int HTTP_CREATED = 201;
+
+ public final static int HTTP_ENTITY_TOO_LARGE = 413;
+
+ public final static int HTTP_EXPECT_FAILED = 417;
+
+ public final static int HTTP_FORBIDDEN = 403;
+
+ public final static int HTTP_GATEWAY_TIMEOUT = 504;
+
+ public final static int HTTP_GONE = 410;
+
+ public final static int HTTP_INTERNAL_ERROR = 500;
+
+ public final static int HTTP_LENGTH_REQUIRED = 411;
+
+ public final static int HTTP_MOVED_PERM = 301;
+
+ public final static int HTTP_MOVED_TEMP = 302;
+
+ public final static int HTTP_MULT_CHOICE = 300;
+
+ public final static int HTTP_NO_CONTENT = 204;
+
+ public final static int HTTP_NOT_ACCEPTABLE = 406;
+
+ public final static int HTTP_NOT_AUTHORITATIVE = 203;
+
+ public final static int HTTP_NOT_FOUND = 404;
+
+ public final static int HTTP_NOT_IMPLEMENTED = 501;
+
+ public final static int HTTP_NOT_MODIFIED = 304;
+
+ public final static int HTTP_OK = 200;
+
+ public final static int HTTP_PARTIAL = 206;
+
+ public final static int HTTP_PAYMENT_REQUIRED = 402;
+
+ public final static int HTTP_PRECON_FAILED = 412;
+
+ public final static int HTTP_PROXY_AUTH = 407;
+
+ public final static int HTTP_REQ_TOO_LONG = 414;
+
+ public final static int HTTP_RESET = 205;
+
+ public final static int HTTP_SEE_OTHER = 303;
+
+ public final static int HTTP_TEMP_REDIRECT = 307;
+
+ public final static int HTTP_UNAUTHORIZED = 401;
+
+ public final static int HTTP_UNAVAILABLE = 503;
+
+ public final static int HTTP_UNSUPPORTED_RANGE = 416;
+
+ public final static int HTTP_UNSUPPORTED_TYPE = 415;
+
+ public final static int HTTP_USE_PROXY = 305;
+
+ public final static int HTTP_VERSION = 505;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/InputConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/InputConnection.java
new file mode 100644
index 0000000..c738f18
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/InputConnection.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface InputConnection extends javax.microedition.io.Connection {
+
+ public abstract java.io.DataInputStream openDataInputStream() throws java.io.IOException;
+
+ public abstract java.io.InputStream openInputStream() throws java.io.IOException;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/OutputConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/OutputConnection.java
new file mode 100644
index 0000000..e03cbea
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/OutputConnection.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface OutputConnection extends javax.microedition.io.Connection {
+
+ public abstract java.io.DataOutputStream openDataOutputStream() throws java.io.IOException;
+
+ public abstract java.io.OutputStream openOutputStream() throws java.io.IOException;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnection.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnection.java
new file mode 100644
index 0000000..aecb893
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnection.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface StreamConnection extends javax.microedition.io.InputConnection, javax.microedition.io.OutputConnection {
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnectionNotifier.java b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnectionNotifier.java
new file mode 100644
index 0000000..9b718bf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/StreamConnectionNotifier.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package javax.microedition.io;
+
+public abstract interface StreamConnectionNotifier extends javax.microedition.io.Connection {
+
+ public abstract javax.microedition.io.StreamConnection acceptAndOpen() throws java.io.IOException;
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/javax/microedition/io/package.html b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/package.html
new file mode 100644
index 0000000..84f3c6f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/javax/microedition/io/package.html
@@ -0,0 +1,9 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html">
+</head>
+<body bgcolor="#FFFFFF">
+The classes for the generic connections.
+</body>
+</html>
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionListener.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionListener.java
new file mode 100644
index 0000000..c698fe2
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionListener.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io;
+
+import java.util.EventListener;
+import javax.microedition.io.Connection;
+
+/**
+ * Interface for a listener that will receive notification when the connection
+ * is created and when is closed.
+ * <p>
+ * Listener is notified for creation of the connection only when the connector
+ * service creates connection that implements only this and the Connection
+ * interface, when the needed IOProvider is not available at the moment of
+ * creation. When the provider becomes available then all registered listeners
+ * will receive event
+ * <p>
+ * <code>CONNECTION_CREATED</code> and the created connection. Event
+ * <code>CONNECTION_CLOSED</code> must be send from every connection that
+ * implements this interface when it is closing.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface ConnectionListener extends EventListener {
+ /**
+ * Constant for event type created
+ */
+ public static final int CONNECTION_CREATED = 0;
+ /**
+ * Constant for event type closed
+ */
+ public static final int CONNECTION_CLOSED = 1;
+
+ /**
+ * Receives notification that a connection has been created or closed.
+ */
+ public void notify(String uri, int eventType, Connection conn);
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionNotifier.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionNotifier.java
new file mode 100644
index 0000000..86029c6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/ConnectionNotifier.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io;
+
+/**
+ * Provides methods for registering listeners for receiving events from a
+ * connection (that implements this interface).
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface ConnectionNotifier {
+ /**
+ * Adds the given listener to the set of listeners that will be notified
+ * when the connection is created or closed.
+ */
+ public void addConnectionListener(ConnectionListener l);
+
+ /**
+ * Removes the given listener from the set of listeners that will be
+ * notified when the connection is created or closed.
+ */
+ public void removeConnectionListener(ConnectionListener l);
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/RandomAccessConnection.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/RandomAccessConnection.java
new file mode 100644
index 0000000..c550e5e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/RandomAccessConnection.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io;
+
+import java.io.*;
+import javax.microedition.io.InputConnection;
+import javax.microedition.io.OutputConnection;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public interface RandomAccessConnection extends InputConnection, OutputConnection, DataInput, DataOutput {
+
+ public long length() throws IOException;
+
+ public long getFilePointer() throws IOException;
+
+ public boolean isSelected();
+
+ public int read() throws IOException;
+
+ public int read(byte[] buff, int off, int len) throws IOException;
+
+ public void seek(long len) throws IOException;
+
+ public void write(int b) throws IOException;
+
+ public void write(byte[] buff, int off, int len) throws IOException;
+
+ public void flush() throws IOException;
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/Activator.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/Activator.java
new file mode 100644
index 0000000..f159f87
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/Activator.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import org.eclipse.equinox.internal.util.ref.Log;
+import org.osgi.framework.*;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class Activator implements BundleActivator {
+
+ public static BundleContext bc = null;
+ private ConnectorServiceImpl connector;
+ private Log log;
+
+ public void start(BundleContext bc) throws BundleException {
+ Activator.bc = bc;
+ log = new Log(bc, false);
+ log.setDebug(getBoolean("equinox.connector.debug"));
+ log.setPrintOnConsole(getBoolean("equinox.connector.console"));
+ if (log.getDebug()) {
+ log.setMaps(TracerConfigConnector.getMap(), null);
+ }
+ connector = new ConnectorServiceImpl(bc, log);
+ }
+
+ public void stop(BundleContext bc) {
+ if (connector != null) {
+ connector.close();
+ connector = null;
+ log.close();
+ }
+ bc = null;
+ }
+
+ public static boolean getBoolean(String property) {
+ String prop = (bc != null) ? bc.getProperty(property) : System.getProperty(property);
+ return ((prop != null) && prop.equalsIgnoreCase("true"));
+ }
+
+ public static int getInteger(String property, int defaultValue) {
+ String prop = (bc != null) ? bc.getProperty(property) : System.getProperty(property);
+ if (prop != null) {
+ try {
+ return Integer.decode(prop).intValue();
+ } catch (NumberFormatException e) {
+ //do nothing
+ }
+ }
+ return defaultValue;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionFactoryListener.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionFactoryListener.java
new file mode 100644
index 0000000..bdce47f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionFactoryListener.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import java.io.IOException;
+import java.util.*;
+import javax.microedition.io.Connection;
+import org.eclipse.equinox.internal.io.impl.PrivilegedRunner.PrivilegedDispatcher;
+import org.osgi.framework.*;
+import org.osgi.service.io.ConnectionFactory;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ConnectionFactoryListener implements ServiceListener, PrivilegedDispatcher {
+ private static Hashtable urlToConN = new Hashtable(5);
+ private BundleContext bc;
+
+ public ConnectionFactoryListener(BundleContext bc) {
+ this.bc = bc;
+
+ try {
+ bc.addServiceListener(this, '(' + Constants.OBJECTCLASS + '=' + ConnectionFactory.class.getName() + ')');
+ } catch (InvalidSyntaxException ex) {
+ // Ignored - syntax is right!
+ }
+ }
+
+ public void close() {
+ bc.removeServiceListener(this);
+
+ if (!urlToConN.isEmpty()) {
+ Vector copyV = new Vector(urlToConN.size());
+
+ for (Enumeration en = urlToConN.elements(); en.hasMoreElements();) {
+ copyV.addElement(en.nextElement());
+ }
+
+ for (Enumeration en = copyV.elements(); en.hasMoreElements();) {
+ ConnectionNotifierImpl cn = (ConnectionNotifierImpl) en.nextElement();
+ try {
+ cn.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ }
+
+ static void removeConnectionNotifier(String url) {
+ urlToConN.remove(url);
+ }
+
+ static int count = 0;
+
+ static Connection getConnectionNotifier(String scheme, String url, int mode, boolean timeouts, String filter, int count) throws IOException {
+ ConnectionNotifierImpl ret = null;
+ synchronized (urlToConN) {
+ if (urlToConN.containsKey(url)) {
+ return (Connection) urlToConN.get(url);
+ }
+
+ ret = new ConnectionNotifierImpl(scheme, url, mode, timeouts, filter);
+ urlToConN.put(url, ret);
+ if (count == ConnectionFactoryListener.count)
+ return ret;
+ }
+
+ Connection c = ConnectorServiceImpl.getConnection(filter, url, mode, timeouts, false);
+ if (c != null) {
+ if (ret.hasListeners())
+ ret.notifyCreated(c);
+ else
+ ret.close();
+ }
+ return c;
+ }
+
+ public void serviceChanged(ServiceEvent event) {
+ if (event.getType() == ServiceEvent.REGISTERED && !urlToConN.isEmpty()) {
+ ServiceReference ref = event.getServiceReference();
+ ConnectionFactory factory = (ConnectionFactory) bc.getService(ref);
+ String[] schemes = (String[]) ref.getProperty(ConnectionFactory.IO_SCHEME);
+
+ Vector toNotify = new Vector(urlToConN.size());
+ synchronized (urlToConN) {
+ for (Enumeration en = urlToConN.elements(); en.hasMoreElements();) {
+ ConnectionNotifierImpl cn = (ConnectionNotifierImpl) en.nextElement();
+ if (match(schemes, cn.scheme))
+ toNotify.addElement(cn);
+ }
+ }
+ for (int i = 0; i < toNotify.size(); i++) {
+ ConnectionNotifierImpl cn = (ConnectionNotifierImpl) toNotify.elementAt(i);
+ try {
+ if (cn.context != null) {
+ PrivilegedRunner.doPrivileged(cn.context, this, 0, cn, factory, null, null);
+ return;
+ }
+ Connection connection = factory.createConnection(cn.url, cn.mode, cn.timeouts);
+ if (cn.hasListeners())
+ cn.notifyCreated(connection);
+ else
+ cn.close();
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ bc.ungetService(ref);
+ }
+ }
+
+ static boolean match(String[] schemes, String scheme) {
+ if (schemes != null && scheme != null) {
+ for (int i = 0; i < schemes.length; i++) {
+ if (scheme.equals(schemes[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.equinox.internal.io.impl.PrivilegedRunner.PrivilegedDispatcher#dispatchPrivileged(int,
+ * java.lang.Object, java.lang.Object, java.lang.Object,
+ * java.lang.Object)
+ */
+ public Object dispatchPrivileged(int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
+ ConnectionNotifierImpl cn = (ConnectionNotifierImpl) arg1;
+ ConnectionFactory factory = (ConnectionFactory) arg2;
+ Connection connection = factory.createConnection(cn.url, cn.mode, cn.timeouts);
+ if (cn.hasListeners()) {
+ cn.notifyCreated(connection);
+ } else {
+ cn.close();
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionNotifierImpl.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionNotifierImpl.java
new file mode 100644
index 0000000..c5fbf4a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectionNotifierImpl.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.microedition.io.Connection;
+import org.eclipse.equinox.internal.io.ConnectionListener;
+import org.eclipse.equinox.internal.io.ConnectionNotifier;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+class ConnectionNotifierImpl implements ConnectionNotifier, Connection {
+ private Vector list;
+ String scheme;
+ int mode;
+ boolean timeouts;
+ String url;
+ String filter;
+
+ boolean notified = false;
+ Object context;
+
+ ConnectionNotifierImpl(String scheme, String url, int mode, boolean timeouts, String filter) {
+ list = new Vector(3, 5);
+ this.scheme = scheme;
+ this.url = url;
+ this.mode = mode;
+ this.timeouts = timeouts;
+ this.filter = filter;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ context = System.getSecurityManager().getSecurityContext();
+ }
+
+ public void addConnectionListener(ConnectionListener l) {
+ long timeStart = 0;
+ if (ConnectorServiceImpl.hasDebug) {
+ ConnectorServiceImpl.debug(16050, l.getClass().getName(), null);
+ timeStart = System.currentTimeMillis();
+ }
+ try {
+ synchronized (list) {
+ if (!notified) {
+ if (!list.contains(l))
+ list.addElement(l);
+ return;
+ }
+ }
+ Connection c = ConnectorServiceImpl.getConnection(filter, url, mode, timeouts, false);
+ if (c != null)
+ l.notify(url, ConnectionListener.CONNECTION_CREATED, c);
+ } catch (Exception exc) {
+ } finally {
+ if (ConnectorServiceImpl.hasDebug) {
+ ConnectorServiceImpl.debug(16051, String.valueOf(System.currentTimeMillis() - timeStart), null);
+ }
+ }
+ }
+
+ public void removeConnectionListener(ConnectionListener l) {
+ if (ConnectorServiceImpl.hasDebug) {
+ ConnectorServiceImpl.debug(16052, l.getClass().getName(), null);
+ }
+ list.removeElement(l);
+ }
+
+ boolean hasListeners() {
+ return !list.isEmpty();
+ }
+
+ void notifyCreated(Connection conn) {
+ notify(ConnectionListener.CONNECTION_CREATED, conn);
+ }
+
+ private void notify(int eventType, Connection conn) {
+ if (eventType != ConnectionListener.CONNECTION_CREATED && eventType != ConnectionListener.CONNECTION_CLOSED) {
+ return;
+ }
+
+ synchronized (list) {
+ if (notified)
+ return;
+ notified = true;
+ }
+ ConnectionFactoryListener.removeConnectionNotifier(url);
+ for (Enumeration en = list.elements(); en.hasMoreElements();) {
+ ConnectionListener l = (ConnectionListener) en.nextElement();
+ l.notify(url, eventType, conn);
+ }
+ }
+
+ public void close() throws IOException {
+ if (ConnectorServiceImpl.hasDebug) {
+ ConnectorServiceImpl.debug(16053, url, null);
+ }
+ synchronized (list) {
+ if (notified)
+ return;
+ notified = true;
+ }
+ ConnectionFactoryListener.removeConnectionNotifier(url);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectorServiceImpl.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectorServiceImpl.java
new file mode 100644
index 0000000..e9217f9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/ConnectorServiceImpl.java
@@ -0,0 +1,401 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import java.io.*;
+import javax.microedition.io.*;
+import org.eclipse.equinox.internal.util.ref.Log;
+import org.osgi.framework.*;
+import org.osgi.service.io.ConnectionFactory;
+import org.osgi.service.io.ConnectorService;
+
+/**
+ * ConnectorService implementation.
+ *
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public class ConnectorServiceImpl implements ConnectorService {
+ static BundleContext bc;
+ ServiceRegistration reg;
+ ConnectionFactoryListener listener;
+
+ static boolean enableNotification = Activator.getBoolean("eclipse.io.enable.notification");
+ public static boolean hasDebug;
+ private static Log log;
+
+ public ConnectorServiceImpl(BundleContext bc, Log log) {
+ ConnectorServiceImpl.log = log;
+ ConnectorServiceImpl.hasDebug = log.getDebug();
+ init(bc);
+ }
+
+ public static void debug(int id, String message, Throwable t) {
+ log.debug(0x1200, id, message, t, false);
+ }
+
+ public ConnectorServiceImpl(BundleContext bc) {
+ init(bc);
+ }
+
+ private void init(BundleContext bc) {
+ ConnectorServiceImpl.bc = bc;
+
+ if (enableNotification) {
+ listener = new ConnectionFactoryListener(bc);
+ }
+
+ reg = bc.registerService(ConnectorService.class.getName(), this, null);
+ }
+
+ public void close() {
+ reg.unregister();
+
+ if (listener != null) {
+ listener.close();
+ }
+ }
+
+ public Connection open(String uri) throws IOException {
+ return open(uri, READ_WRITE);
+ }
+
+ public Connection open(String uri, int mode) throws IOException {
+ return open(uri, mode, false);
+ }
+
+ static char[] chars = {'~', '='};
+
+ public Connection open(String uri, int mode, boolean timeouts) throws IOException {
+ long timeStart = 0;
+ if (hasDebug) {
+ debug(16001, uri + ", " + (mode == READ_WRITE ? "READ_WRITE" : (mode == READ) ? "READ" : "WRITE"), null);
+ timeStart = System.currentTimeMillis();
+ }
+ try {
+ if (uri == null) {
+ throw new IllegalArgumentException("URL cannot be NULL!");
+ }
+
+ int sPos = uri.indexOf(":");
+
+ if (sPos < 1) { // scheme must be at least with 1 symbol
+ throw new IllegalArgumentException("Does not have scheme");
+ }
+
+ String scheme = uri.substring(0, sPos);
+ StringBuffer filter = new StringBuffer(scheme.length() + 13);
+ filter.append('(');
+ filter.append(ConnectionFactory.IO_SCHEME);
+ filter.append(chars);
+ filter.append(scheme);
+ filter.append(')');
+
+ int count = 0;
+ if (listener != null)
+ count = ConnectionFactoryListener.count;
+ Connection c = getConnection(filter.toString(), uri, mode, timeouts, true);
+
+ if (c == null && listener != null)
+ c = ConnectionFactoryListener.getConnectionNotifier(scheme, uri, mode, timeouts, filter.toString(), count);
+ if (c == null)
+ throw new ConnectionNotFoundException("Failed to create connection " + uri);
+ return c;
+ } finally {
+ if (hasDebug) {
+ debug(16002, String.valueOf(System.currentTimeMillis() - timeStart), null);
+ }
+ }
+ }
+
+ static protected Connection getConnection(String filter, String uri, int mode, boolean timeouts, boolean connector) throws IOException {
+ ServiceReference[] cfRefs = null;
+ Connection ret = null;
+ try {
+ cfRefs = bc.getServiceReferences(ConnectionFactory.class.getName(), filter);
+ } catch (InvalidSyntaxException ex) {
+ }
+
+ if (cfRefs != null) {
+ sort(cfRefs, 0, cfRefs.length);
+ }
+
+ IOException ioExc = null;
+ boolean not_found = false;
+ if (cfRefs != null)
+ for (int i = 0; i < cfRefs.length; i++) {
+ ConnectionFactory prov = (ConnectionFactory) bc.getService(cfRefs[i]);
+ if (prov != null)
+ try {
+ not_found = true;
+ ret = prov.createConnection(uri, mode, timeouts);
+ } catch (IOException e) {
+ if (hasDebug) {
+ debug(16003, prov.getClass().getName(), e);
+ }
+ if (ioExc == null)
+ ioExc = e;
+ } finally {
+ if (ret == null)
+ bc.ungetService(cfRefs[i]);
+ else {
+ if (hasDebug) {
+ debug(16004, prov.getClass().getName(), null);
+ }
+ return ret;
+ }
+ }
+ }
+
+ if (connector)
+ try {
+ ret = Connector.open(uri, mode, timeouts);
+ } catch (ConnectionNotFoundException ignore) { // returns null ->
+ // ConnectionNotifier
+ // is created
+ debug(16014, null, ignore);
+ } finally {
+ if (ret == null) {
+ if (ioExc != null)
+ throw ioExc;
+ if (not_found)
+ throw new ConnectionNotFoundException("Failed to create connection " + uri);
+
+ } else {
+ if (hasDebug) {
+ debug(16005, null, null);
+ }
+ }
+ }
+ return ret;
+ }
+
+ private static void sort(ServiceReference[] array, int start, int end) {
+ int middle = (start + end) / 2;
+ if (start + 1 < middle)
+ sort(array, start, middle);
+ if (middle + 1 < end)
+ sort(array, middle, end);
+ if (start + 1 >= end)
+ return; // this case can only happen when this method is called by
+ // the user
+
+ if (getRanking(array[middle - 1]) == getRanking(array[middle])) {
+ if (getServiceID(array[middle - 1]) < getServiceID(array[middle])) {
+ return;
+ }
+ } else if (getRanking(array[middle - 1]) >= getRanking(array[middle])) {
+ return;
+ }
+
+ if (start + 2 == end) {
+ ServiceReference temp = array[start];
+ array[start] = array[middle];
+ array[middle] = temp;
+ return;
+ }
+ int i1 = start, i2 = middle, i3 = 0;
+ Object[] merge = new Object[end - start];
+ while (i1 < middle && i2 < end) {
+ if (getRanking(array[i1]) == getRanking(array[i2])) {
+ merge[i3++] = getServiceID(array[i1]) < getServiceID(array[i2]) ? array[i1++] : array[i2++];
+ } else {
+ merge[i3++] = getRanking(array[i1]) >= getRanking(array[i2]) ? array[i1++] : array[i2++];
+ }
+ }
+ if (i1 < middle)
+ System.arraycopy(array, i1, merge, i3, middle - i1);
+ System.arraycopy(merge, 0, array, start, i2 - start);
+ }
+
+ private static int getRanking(ServiceReference ref) {
+ Object rank = ref.getProperty(Constants.SERVICE_RANKING);
+
+ if (rank == null || !(rank instanceof Integer)) {
+ return 0;
+ }
+
+ return ((Integer) rank).intValue();
+ }
+
+ private static long getServiceID(ServiceReference ref) {
+ Object sid = ref.getProperty(Constants.SERVICE_ID);
+
+ if (sid == null || !(sid instanceof Long)) {
+ return 0;
+ }
+
+ return ((Long) sid).intValue();
+ }
+
+ /**
+ * Create and open an <tt>DataInputStream</tt> object for the specified
+ * name.
+ *
+ * @param name
+ * the URI for the connection.
+ * @throws IOException
+ * if and I/O error occurs
+ * @throws ConnectionNotFoundException
+ * if the <tt>Connection</tt> object can not be made or if no
+ * handler for the requested scheme can be found.
+ * @throws IllegalArgumentException
+ * if the given uri is invalid
+ * @return A <tt>DataInputStream</tt> to the given URI
+ */
+ public DataInputStream openDataInputStream(String name) throws IOException {
+ long timeBegin = 0;
+ if (hasDebug) {
+ debug(16006, name, null);
+ timeBegin = System.currentTimeMillis();
+ }
+ try {
+ Connection conn = open(name, READ);
+
+ if (!(conn instanceof InputConnection)) {
+ try {
+ conn.close();
+ } catch (IOException e) {
+ }
+ throw new IOException("Connection does not implement InputConnection:" + conn.getClass());
+ }
+
+ return ((InputConnection) conn).openDataInputStream();
+ } finally {
+ if (hasDebug) {
+ debug(16007, String.valueOf(System.currentTimeMillis() - timeBegin), null);
+ }
+ }
+ }
+
+ /**
+ * Create and open an <tt>DataOutputStream</tt> object for the specified
+ * name.
+ *
+ * @param name
+ * the URI for the connection.
+ * @throws IOException
+ * if and I/O error occurs
+ * @throws ConnectionNotFoundException
+ * if the <tt>Connection</tt> object can not be made or if no
+ * handler for the requested scheme can be found.
+ * @throws IllegalArgumentException
+ * if the given uri is invalid
+ * @return A <tt>DataOutputStream</tt> to the given URI
+ */
+ public DataOutputStream openDataOutputStream(String name) throws IOException {
+ long timeBegin = 0;
+ if (hasDebug) {
+ debug(16008, name, null);
+ }
+ try {
+ Connection conn = open(name, WRITE);
+
+ if (!(conn instanceof OutputConnection)) {
+ try {
+ conn.close();
+ } catch (IOException e) {
+ }
+
+ throw new IOException("Connection does not implement OutputConnection:" + conn.getClass());
+ }
+
+ return ((OutputConnection) conn).openDataOutputStream();
+ } finally {
+ if (hasDebug) {
+ debug(16009, String.valueOf(System.currentTimeMillis() - timeBegin), null);
+ }
+ }
+ }
+
+ /**
+ * Create and open an <tt>InputStream</tt> object for the specified name.
+ *
+ *
+ * @param name
+ * the URI for the connection.
+ * @throws IOException
+ * if and I/O error occurs
+ * @throws ConnectionNotFoundException
+ * if the <tt>Connection</tt> object can not be made or if no
+ * handler for the requested scheme can be found.
+ * @throws IllegalArgumentException
+ * if the given uri is invalid
+ * @return A <tt>InputStream</tt> to the given URI
+ */
+ public InputStream openInputStream(String name) throws IOException {
+ long timeBegin = 0;
+ if (hasDebug) {
+ debug(16010, name, null);
+ timeBegin = System.currentTimeMillis();
+ }
+ try {
+ Connection conn = open(name, READ);
+
+ if (!(conn instanceof InputConnection)) {
+ try {
+ conn.close();
+ } catch (IOException e) {
+ }
+
+ throw new IOException("Connection does not implement InputConnection:" + conn.getClass());
+ }
+
+ return ((InputConnection) conn).openInputStream();
+ } finally {
+ if (hasDebug) {
+ debug(16011, String.valueOf(System.currentTimeMillis() - timeBegin), null);
+ }
+ }
+ }
+
+ /**
+ * Create and open an <tt>OutputStream</tt> object for the specified name.
+ *
+ * @param name
+ * the URI for the connection.
+ * @throws IOException
+ * if and I/O error occurs
+ * @throws ConnectionNotFoundException
+ * if the <tt>Connection</tt> object can not be made or if no
+ * handler for the requested scheme can be found.
+ * @throws IllegalArgumentException
+ * if the given uri is invalid
+ * @return A <tt>OutputStream</tt> to the given URI
+ */
+ public OutputStream openOutputStream(String name) throws IOException {
+ long timeBegin = 0;
+ if (hasDebug) {
+ debug(16012, name, null);
+ timeBegin = System.currentTimeMillis();
+ }
+ try {
+ Connection conn = open(name, WRITE);
+
+ if (!(conn instanceof OutputConnection)) {
+ try {
+ conn.close();
+ } catch (IOException e) {
+ }
+
+ throw new IOException("Connection does not implement OutputConnection:" + conn.getClass());
+ }
+
+ return ((OutputConnection) conn).openOutputStream();
+ } finally {
+ if (hasDebug) {
+ debug(16013, String.valueOf(System.currentTimeMillis() - timeBegin), null);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/PrivilegedRunner.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/PrivilegedRunner.java
new file mode 100644
index 0000000..485724e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/PrivilegedRunner.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import java.security.*;
+import org.eclipse.equinox.internal.util.pool.ObjectCreator;
+import org.eclipse.equinox.internal.util.pool.ObjectPool;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public final class PrivilegedRunner implements ObjectCreator {
+
+ private static ObjectPool POOL;
+
+ static {
+ try {
+ POOL = new ObjectPool(new PrivilegedRunner(), 5, 10);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /* prevent instantiations */
+ private PrivilegedRunner() {
+ }
+
+ /**
+ * Same as the longer doPrivileged method, but fills in the first parameter
+ * only. All other parameters are set to <code>null</code>.
+ *
+ * @param dispatcher
+ * the dispatcher which should be called
+ * @param type
+ * the type of the action - used in the dispatcher
+ * @param arg1
+ * a parameter received by the dispatcher
+ * @see #doPrivileged(PrivilegedDispatcher, int, Object, Object, Object,
+ * Object)
+ * @return the object returned from the execution
+ * @throws Exception
+ * if the dispatcher fails
+ */
+ public static final Object doPrivileged(PrivilegedDispatcher dispatcher, int type, Object arg1) throws Exception {
+ return doPrivileged(null, dispatcher, type, arg1, null, null, null);
+ }
+
+ /**
+ * Performs a privileged action. The method calls the dispatcher inside the
+ * privileged call passing it the same parameters that were passed to this
+ * method.
+ *
+ * @param dispatcher
+ * the dispatcher which should be called
+ * @param type
+ * the type of the action - used in the dispatcher
+ * @param arg1
+ * a parameter received by the dispatcher
+ * @param arg2
+ * a parameter received by the dispatcher
+ * @param arg3
+ * a parameter received by the dispatcher
+ * @param arg4
+ * a parameter received by the dispatcher
+ * @return the object returned from the execution
+ * @throws Exception
+ * if the dispatcher fails
+ */
+ public static final Object doPrivileged(PrivilegedDispatcher dispatcher, int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
+ return doPrivileged(null, dispatcher, type, arg1, arg2, arg3, arg4);
+ }
+
+ /**
+ * Performs a privileged action. The method calls the dispatcher inside the
+ * privileged call passing it the same parameters that were passed to this
+ * method.
+ *
+ * @param context
+ * the access context
+ * @param dispatcher
+ * the dispatcher which should be called
+ * @param type
+ * the type of the action - used in the dispatcher
+ * @param arg1
+ * a parameter received by the dispatcher
+ * @param arg2
+ * a parameter received by the dispatcher
+ * @param arg3
+ * a parameter received by the dispatcher
+ * @param arg4
+ * a parameter received by the dispatcher
+ * @return the object returned from the execution
+ * @throws Exception
+ * if the dispatcher fails
+ */
+ public static final Object doPrivileged(Object context, PrivilegedDispatcher dispatcher, int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
+ /* init runner */
+ PA runner = (PA) POOL.getObject();
+ runner.dispatcher = dispatcher;
+ runner.type = type;
+ runner.arg1 = arg1;
+ runner.arg2 = arg2;
+ runner.arg3 = arg3;
+ runner.arg4 = arg4;
+
+ try {
+ if (System.getSecurityManager() != null) {
+ /*
+ * if security manager is set - then privileged execution is
+ * started
+ */
+ return (context != null)
+ //
+ ? AccessController.doPrivileged(runner, (AccessControlContext) context)
+ : AccessController.doPrivileged(runner);
+ }
+ /* if no security manager is set - simply run the action */
+ return runner.run();
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ } finally {
+ runner.recycle();
+ POOL.releaseObject(runner);
+ }
+ }
+
+ /**
+ * @see org.eclipse.equinox.internal.util.pool.ObjectCreator#getInstance()
+ */
+ public Object getInstance() throws Exception {
+ return new PA();
+ }
+
+ /**
+ * This dispatcher is the handler that is called within the privileged call.
+ * It should dispatch and perform the requested actions depending on the
+ * action type and using the given job parameters.
+ *
+ * @author Valentin Valchev
+ * @version $Revision: 1.3 $
+ */
+ public static interface PrivilegedDispatcher {
+
+ /**
+ * @param type
+ * the type of the action
+ * @param arg1
+ * parameter 1 - depends on the action type
+ * @param arg2
+ * parameter 2 - depends on the action type
+ * @param arg3
+ * parameter 3 - depends on the action type
+ * @param arg4
+ * parameter 4 - depends on the action type
+ * @return an object which should be returned from the
+ * PrivilegedAction.run() method
+ * @throws Exception
+ * on error
+ */
+ Object dispatchPrivileged(int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception;
+ }
+
+ static class PA implements PrivilegedExceptionAction {
+
+ int type;
+ Object arg1, arg2, arg3, arg4;
+ PrivilegedDispatcher dispatcher;
+
+ void recycle() {
+ dispatcher = null;
+ type = -1;
+ arg1 = arg2 = arg3 = arg4 = null;
+ }
+
+ /**
+ * @see java.security.PrivilegedExceptionAction#run()
+ */
+ public Object run() throws Exception {
+ return dispatcher.dispatchPrivileged(type, arg1, arg2, arg3, arg4);
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/TracerConfigConnector.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/TracerConfigConnector.java
new file mode 100644
index 0000000..caa5445
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/impl/TracerConfigConnector.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.impl;
+
+import org.eclipse.equinox.internal.util.hash.HashIntObjNS;
+
+/**
+ * @author Lubomir Mitev
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+public class TracerConfigConnector {
+
+ public static HashIntObjNS getMap() {
+ /*
+ * increase size if you add more dumps in the table - at this moment the
+ * entries in table are 159
+ */
+ HashIntObjNS map = new HashIntObjNS(28);// (int)(28 * 0.7) = 19, 18
+ constructMap(map);
+ return map;
+ }
+
+ public static void constructMap(HashIntObjNS map) {
+ map.put(-0x1200, "Connector Service");
+ map.put(16001, "[BEGIN - open connection] The values of the URI and the access mode are");
+ map.put(16002, "[END - open connection] took");
+ map.put(16003, "IOException when connection is created from ConnectionFactory");
+ map.put(16004, "Connection is created from ConnectionFactory");
+ map.put(16005, "Connection is created from javax.microedition.io.Connector");
+ map.put(16006, "[BEGIN - open DataInputStream] The URI is");
+ map.put(16007, "[END - open DataInputStream] took");
+ map.put(16008, "[BEGIN - open DataOutputStream] The URI is");
+ map.put(16009, "[END - open DataOutputStream] took");
+ map.put(16010, "[BEGIN - open InputStream] The URI is");
+ map.put(16011, "[END - open InputStream] took");
+ map.put(16012, "[BEGIN - open OutputStream] The URI is");
+ map.put(16013, "[END - open OutputStream] took");
+ map.put(16014, "ConnectionNotFoundException thrown from system javax.microedition.io.Connector");
+ map.put(16050, "[BEGIN - add ConnectionListener] The ConnectionListener is");
+ map.put(16051, "[END - add ConnectionListener] took");
+ map.put(16052, "Remove ConnectionListener");
+ map.put(16053, "Close connection. The URI is");
+ }
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/package.html b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/package.html
new file mode 100644
index 0000000..32d3d84
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/package.html
@@ -0,0 +1,10 @@
+<!-- $Header: /cvsroot/eclipse/equinox-incubator/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/package.html,v 1.1 2007/11/30 13:45:13 pdobrev Exp $ -->
+<BODY>
+<P>The OSGi IO Provider Specification Version 1.0.
+<p>Bundles wishing to use this package must list the package
+in the <TT>Import-Package</TT> header of the bundle's manifest.
+For example:
+<pre>
+Import-Package: org.osgi.service.io; specification-version=1.0
+</pre>
+</BODY>
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractConnectionNotifier.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractConnectionNotifier.java
new file mode 100644
index 0000000..5cf2968
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractConnectionNotifier.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.util;
+
+import java.util.*;
+import javax.microedition.io.Connection;
+import org.eclipse.equinox.internal.io.ConnectionListener;
+import org.eclipse.equinox.internal.io.ConnectionNotifier;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public abstract class AbstractConnectionNotifier extends Dictionary implements ConnectionNotifier {
+ private Vector list;
+ private Hashtable info;
+
+ // the local address to which the connection is bound
+
+ public final static String LOCAL_ADDRESS = "local_address";
+
+ // the local port to which the connection is bound.
+
+ public final static String LOCAL_PORT = "local_port";
+
+ // the remote address to which the connection is connected.
+
+ public final static String ADDRESS = "address";
+
+ // the remote port to which the connection is connected.
+
+ public final static String PORT = "port";
+
+ // the encoder used
+
+ public final static String ENCODER = "enc";
+
+ public static final String SO_TIMEOUT = "timeout";
+
+ protected AbstractConnectionNotifier() {
+ list = new Vector(2, 3);
+ info = new Hashtable(30);
+ }
+
+ // ConnectionNotifier methods
+ public void addConnectionListener(ConnectionListener l) {
+ if (!list.contains(l)) {
+ list.addElement(l);
+ }
+ }
+
+ public void removeConnectionListener(ConnectionListener l) {
+ list.removeElement(l);
+ }
+
+ // returns the url of connection
+ protected abstract String getURL();
+
+ // the connection to which this ConnectionLife is bound to
+ protected abstract Connection getConnection();
+
+ protected void notifyClosed() {
+ for (Enumeration en = list.elements(); en.hasMoreElements();) {
+ ConnectionListener l = (ConnectionListener) en.nextElement();
+ l.notify(getURL(), ConnectionListener.CONNECTION_CLOSED, getConnection());
+ }
+ }
+
+ protected void setInfo(String key, Object value) {
+ if (key != null && value != null) {
+ info.put(key, value);
+ }
+ }
+
+ public int size() {
+ return info.size();
+ }
+
+ public boolean isEmpty() {
+ return info.isEmpty();
+ }
+
+ public Enumeration keys() {
+ return info.keys();
+ }
+
+ public Enumeration elements() {
+ return info.elements();
+ }
+
+ public Object get(Object key) {
+ return info.get(key);
+ }
+
+ public Object remove(Object key) {
+ return info.remove(key);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractDatagram.java b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractDatagram.java
new file mode 100644
index 0000000..4f81da4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.io/src/org/eclipse/equinox/internal/io/util/AbstractDatagram.java
@@ -0,0 +1,434 @@
+/*******************************************************************************
+ * Copyright (c) 1997-2007 by ProSyst Software GmbH
+ * http://www.prosyst.com
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * ProSyst Software GmbH - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.io.util;
+
+import java.io.*;
+import javax.microedition.io.Datagram;
+
+/**
+ * @author Pavlin Dobrev
+ * @version 1.0
+ */
+
+public abstract class AbstractDatagram implements Datagram {
+ protected byte[] data;
+ private int pos;
+ private int count;
+
+ public AbstractDatagram(int size) {
+ this(new byte[size]);
+ }
+
+ public AbstractDatagram(byte[] data) {
+ this.data = data;
+ pos = 0;
+ count = data.length;
+ }
+
+ public AbstractDatagram(byte[] data, int start, int length) {
+ if (start > data.length || (start + length) > data.length) {
+ throw new IllegalArgumentException("Start possition or length is greater than data length");
+ }
+
+ this.data = data;
+ pos = start;
+ count = length;
+ }
+
+ public synchronized byte[] getData() {
+ return data;
+ }
+
+ public int getLength() {
+ return count;
+ }
+
+ public int getOffset() {
+ return pos;
+ }
+
+ public void setLength(int len) {
+ if (len < 0 || len > data.length) {
+ throw new IllegalArgumentException("Given length is negative or greater than buffer size");
+ }
+
+ count = len;
+ }
+
+ public synchronized void setData(byte[] buffer, int offset, int len) {
+ if (offset > buffer.length || (offset + len) > buffer.length) {
+ throw new IllegalArgumentException();
+ }
+
+ data = buffer;
+ pos = offset;
+ count = len;
+ }
+
+ public void reset() {
+ pos = 0;
+ count = 0;
+ }
+
+ public int read() {
+ return (pos < count) ? (data[pos++] & 0xff) : -1;
+ }
+
+ public void readFully(byte b[]) throws IOException {
+ readFully(b, 0, b.length);
+ }
+
+ public void readFully(byte b[], int off, int len) throws IOException {
+ if (len < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ int n = 0;
+
+ while (n < len) {
+ int ch = read();
+
+ if (ch < 0) {
+ throw new EOFException();
+ }
+
+ b[off + (n++)] = (byte) ch;
+ }
+ }
+
+ public int skipBytes(int n) throws IOException {
+ if (pos + n > count) {
+ n = count - pos;
+ }
+
+ if (n < 0) {
+ return 0;
+ }
+
+ pos += n;
+ return n;
+ }
+
+ public boolean readBoolean() throws IOException {
+ int ch = read();
+
+ if (ch < 0) {
+ throw new EOFException();
+ }
+
+ return (ch != 0);
+ }
+
+ public byte readByte() throws IOException {
+ int ch = read();
+
+ if (ch < 0) {
+ throw new EOFException();
+ }
+
+ return (byte) (ch);
+ }
+
+ public int readUnsignedByte() throws IOException {
+ int ch = read();
+
+ if (ch < 0) {
+ throw new EOFException();
+ }
+
+ return ch;
+ }
+
+ public short readShort() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+
+ return (short) ((ch1 << 8) + (ch2 << 0));
+ }
+
+ public int readUnsignedShort() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+
+ return (ch1 << 8) + (ch2 << 0);
+ }
+
+ public char readChar() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+
+ return (char) ((ch1 << 8) + (ch2 << 0));
+ }
+
+ public int readInt() throws IOException {
+ int ch1 = read();
+ int ch2 = read();
+ int ch3 = read();
+ int ch4 = read();
+
+ if ((ch1 | ch2 | ch3 | ch4) < 0) {
+ throw new EOFException();
+ }
+
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+ }
+
+ public long readLong() throws IOException {
+ return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
+ }
+
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public String readLine() throws IOException {
+ throw new RuntimeException("Function not supported");
+ }
+
+ public String readUTF() throws IOException {
+ return readUTF(this);
+ }
+
+ public final static String readUTF(DataInput in) throws IOException {
+ int utflen = in.readUnsignedShort();
+ StringBuffer str = new StringBuffer(utflen);
+ byte bytearr[] = new byte[utflen];
+ int c, char2, char3;
+ int count = 0;
+
+ in.readFully(bytearr, 0, utflen);
+
+ while (count < utflen) {
+ c = bytearr[count] & 0xff;
+
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ case 2 :
+ case 3 :
+ case 4 :
+ case 5 :
+ case 6 :
+ case 7 :
+ /* 0xxxxxxx */
+ count++;
+ str.append((char) c);
+ break;
+
+ case 12 :
+ case 13 :
+ /* 110x xxxx 10xx xxxx */
+ count += 2;
+
+ if (count > utflen) {
+ throw new UTFDataFormatException();
+ }
+
+ char2 = bytearr[count - 1];
+
+ if ((char2 & 0xC0) != 0x80) {
+ throw new UTFDataFormatException();
+ }
+
+ str.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F)));
+ break;
+
+ case 14 :
+ /* 1110 xxxx 10xx xxxx 10xx xxxx */
+ count += 3;
+
+ if (count > utflen) {
+ throw new UTFDataFormatException();
+ }
+
+ char2 = bytearr[count - 2];
+ char3 = bytearr[count - 1];
+
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
+ throw new UTFDataFormatException();
+ }
+
+ str.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)));
+ break;
+
+ default :
+ /* 10xx xxxx, 1111 xxxx */
+ throw new UTFDataFormatException();
+ }
+ }
+
+ // The number of chars produced may be less than utflen
+ return str.toString();