diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.classpath b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.classpath new file mode 100644 index 00000000000..375961e4d61 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.gitignore b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.gitignore new file mode 100644 index 00000000000..14435d8efa9 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.gitignore @@ -0,0 +1 @@ +OSGI-INF/org.eclipse.*.xml diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.project b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.project new file mode 100644 index 00000000000..c4b531be21f --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.project @@ -0,0 +1,34 @@ + + + org.eclipse.core.runtime.bundlegroups + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.resources.prefs b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.runtime.prefs b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 00000000000..5a0ad22d2a7 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\n diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.core.prefs b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..3a8fc308afb --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,126 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=17 +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.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +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.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +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.includeNullInfoFromAsserts=disabled +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=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=ignore +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +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.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=error +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.ui.prefs b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..81f11511252 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,147 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 +sp_cleanup.add_all=false +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_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.also_simplify_lambda=true +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.array_with_curly=false +sp_cleanup.arrays_fill=false +sp_cleanup.bitwise_conditional_expression=false +sp_cleanup.boolean_literal=false +sp_cleanup.boolean_value_rather_than_comparison=true +sp_cleanup.break_loop=false +sp_cleanup.collection_cloning=false +sp_cleanup.comparing_on_criteria=false +sp_cleanup.comparison_statement=false +sp_cleanup.controlflow_merge=false +sp_cleanup.convert_functional_interfaces=true +sp_cleanup.convert_to_enhanced_for_loop=true +sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true +sp_cleanup.convert_to_switch_expressions=false +sp_cleanup.correct_indentation=false +sp_cleanup.do_while_rather_than_while=false +sp_cleanup.double_negation=false +sp_cleanup.else_if=false +sp_cleanup.embedded_if=false +sp_cleanup.evaluate_nullable=false +sp_cleanup.extract_increment=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=true +sp_cleanup.hash=false +sp_cleanup.if_condition=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.instanceof=false +sp_cleanup.instanceof_keyword=false +sp_cleanup.invert_equals=false +sp_cleanup.join=false +sp_cleanup.lazy_logical_operator=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.map_cloning=false +sp_cleanup.merge_conditional_blocks=false +sp_cleanup.multi_catch=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.no_string_creation=false +sp_cleanup.no_super=false +sp_cleanup.number_suffix=false +sp_cleanup.objects_equals=false +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false +sp_cleanup.operand_factorization=false +sp_cleanup.organize_imports=true +sp_cleanup.overridden_assignment=false +sp_cleanup.overridden_assignment_move_decl=true +sp_cleanup.plain_replacement=false +sp_cleanup.precompile_regex=false +sp_cleanup.primitive_comparison=false +sp_cleanup.primitive_parsing=false +sp_cleanup.primitive_rather_than_wrapper=true +sp_cleanup.primitive_serialization=false +sp_cleanup.pull_out_if_from_if_else=false +sp_cleanup.pull_up_assignment=false +sp_cleanup.push_down_negation=false +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=true +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.reduce_indentation=false +sp_cleanup.redundant_comparator=false +sp_cleanup.redundant_falling_through_block_end=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_modifiers=false +sp_cleanup.remove_redundant_semicolons=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_array_creation=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_method_parameters=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.replace_deprecated_calls=false +sp_cleanup.return_expression=false +sp_cleanup.simplify_lambda_expression_and_method_ref=true +sp_cleanup.single_used_field=false +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.standard_comparison=false +sp_cleanup.static_inner_class=false +sp_cleanup.strictly_equal_or_different=false +sp_cleanup.stringbuffer_to_stringbuilder=false +sp_cleanup.stringbuilder=false +sp_cleanup.stringbuilder_for_local_vars=true +sp_cleanup.stringconcat_stringbuffer_stringbuilder=false +sp_cleanup.stringconcat_to_textblock=false +sp_cleanup.substring=false +sp_cleanup.switch=false +sp_cleanup.system_property=false +sp_cleanup.system_property_boolean=false +sp_cleanup.system_property_file_encoding=false +sp_cleanup.system_property_file_separator=false +sp_cleanup.system_property_line_separator=false +sp_cleanup.system_property_path_separator=false +sp_cleanup.ternary_operator=false +sp_cleanup.try_with_resource=false +sp_cleanup.unlooped_while=false +sp_cleanup.unreachable_block=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_autoboxing=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_directly_map_method=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_string_is_blank=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 +sp_cleanup.use_unboxing=false +sp_cleanup.use_var=false +sp_cleanup.useless_continue=false +sp_cleanup.useless_return=false +sp_cleanup.valueof_rather_than_instantiation=false diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.pde.ds.annotations.prefs b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.pde.ds.annotations.prefs new file mode 100644 index 00000000000..5faf08b7d5c --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/.settings/org.eclipse.pde.ds.annotations.prefs @@ -0,0 +1,7 @@ +dsVersion=V1_4 +eclipse.preferences.version=1 +enabled=true +generateBundleActivationPolicyLazy=true +path=OSGI-INF +validationErrorLevel=error +validationErrorLevel.missingImplicitUnbindMethod=error diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..ca423529e0a --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.core.runtime.bundlegroups;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime;bundle-version="3.29.0" +Bundle-RequiredExecutionEnvironment: JavaSE-21 +Import-Package: javax.xml.parsers, + org.w3c.dom, + org.xml.sax, + org.xml.sax.helpers +Bundle-ActivationPolicy: lazy +Automatic-Module-Name: org.eclipse.core.runtime.bundlegroups +Service-Component: OSGI-INF/org.eclipse.core.internal.runtime.bundlegroups.FeatureBundleGroupProvider.xml diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/about.html b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/about.html new file mode 100644 index 00000000000..164f781a8fd --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/about.html @@ -0,0 +1,36 @@ + + + + +About + + +

About This Content

+ +

November 30, 2017

+

License

+ +

+ 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 2.0 ("EPL"). A copy of the EPL is + available at http://www.eclipse.org/legal/epl-2.0. + For purposes of the EPL, "Program" will mean the Content. +

+ +

+ 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 http://www.eclipse.org. +

+ + + \ No newline at end of file diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/build.properties b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/build.properties new file mode 100644 index 00000000000..9b2f9357689 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/build.properties @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 2026 Lars Vogel and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Lars Vogel - initial API and implementation +############################################################################### +bin.includes = META-INF/,\ + .,\ + about.html,\ + plugin.properties,\ + OSGI-INF/ +src.includes = about.html +source.. = src/ +output.. = bin/ diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/plugin.properties b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/plugin.properties new file mode 100644 index 00000000000..756b8e0331e --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/plugin.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2026 Lars Vogel and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Lars Vogel - initial API and implementation +############################################################################### +pluginName= Installed Feature Bundle Groups +providerName= Eclipse.org diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/AboutInfo.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/AboutInfo.java new file mode 100644 index 00000000000..0169c63b8ee --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/AboutInfo.java @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.net.URL; +import java.util.Hashtable; + +import org.eclipse.core.runtime.IPath; + + +/** + * The information within this object is obtained from the about INI file. + * This file resides within an install configurations directory and must be a + * standard java property file. + *

+ * This class is not intended to be instantiated or subclassed by clients. + *

+ */ +public final class AboutInfo { + private final static String INI_FILENAME = "about.ini"; //$NON-NLS-1$ + private final static String PROPERTIES_FILENAME = "about.properties"; //$NON-NLS-1$ + private final static String MAPPINGS_FILENAME = "about.mappings"; //$NON-NLS-1$ + + private final String featureId; + private String versionId = ""; //$NON-NLS-1$ + private String featurePluginLabel; + private String providerName; + private String appName; + private URL windowImageURL; + private URL[] windowImagesURLs; + private URL aboutImageURL; + private URL featureImageURL; + private URL welcomePageURL; + private String aboutText; + private String welcomePerspective; + private String tipsAndTricksHref; + + + /* + * Create a new about info for a feature with the given id. + */ + /* package */ AboutInfo(String featureId) { + super(); + this.featureId = featureId; + } + + /** + * Returns the configuration information for the feature with the + * given id. + * + * @param featureId the feature id + * @param versionId the version id (of the feature) + * @param pluginId the plug-in id + * @return the configuration information for the feature + */ + public static AboutInfo readFeatureInfo(String featureId, String versionId, String pluginId) { +// Assert.isNotNull(featureId); +// Assert.isNotNull(versionId); +// Assert.isNotNull(pluginId); + IniFileReader reader = new IniFileReader(featureId, pluginId, INI_FILENAME, PROPERTIES_FILENAME, MAPPINGS_FILENAME); + reader.load(); +// bug 78031 +// if (!status.isOK()) { +// //return null; +// return new AboutInfo(featureId); // dummy about info +// } + + AboutInfo info = new AboutInfo(featureId); + Hashtable runtimeMappings = new Hashtable<>(); + runtimeMappings.put("{featureVersion}", versionId); //$NON-NLS-1$ + info.versionId = versionId; + info.featurePluginLabel = reader.getFeaturePluginLabel(); + info.providerName = reader.getProviderName(); + info.appName = reader.getString("appName", true, runtimeMappings); //$NON-NLS-1$ + info.aboutText = reader.getString("aboutText", true, runtimeMappings); //$NON-NLS-1$ + info.windowImageURL = reader.getURL("windowImage"); //$NON-NLS-1$ + // look for the newer array, but if its not there then use the older, + // single image definition + info.windowImagesURLs = reader.getURLs("windowImages"); //$NON-NLS-1$ + info.aboutImageURL = reader.getURL("aboutImage"); //$NON-NLS-1$ + info.featureImageURL = reader.getURL("featureImage"); //$NON-NLS-1$ + info.welcomePageURL = reader.getURL("welcomePage"); //$NON-NLS-1$ + info.welcomePerspective = reader.getString("welcomePerspective", false, runtimeMappings); //$NON-NLS-1$ + info.tipsAndTricksHref = reader.getString("tipsAndTricksHref", false, runtimeMappings); //$NON-NLS-1$ + return info; + } + + /** + * Returns the URL for an image which can be shown in an "about" dialog + * for this product. Products designed to run "headless" typically would not + * have such an image. + * + * @return the URL for an about image, or null if none + */ + public URL getAboutImageURL() { + return aboutImageURL; + } + + /** + * Returns the URL for an image which can be shown in an "about features" + * dialog. Products designed to run "headless" typically would not have such an image. + * + * @return the URL for a feature image, or null if none + */ + public URL getFeatureImageURL() { + return featureImageURL; + } + + /** + * Returns the simple name of the feature image file. + * + * @return the simple name of the feature image file, + * or null if none + */ + public String getFeatureImageName() { + if (featureImageURL != null) { + IPath path = IPath.fromOSString(featureImageURL.getPath()); + return path.lastSegment(); + } + return null; + } + + + /** + * Returns a label for the feature plugin, or null. + */ + public String getFeatureLabel() { + return featurePluginLabel; + } + + /** + * Returns the id for this feature. + * + * @return the feature id + */ + public String getFeatureId() { + return featureId; + } + + /** + * Returns the text to show in an "about" dialog for this product. + * Products designed to run "headless" typically would not have such text. + * + * @return the about text, or null if none + */ + public String getAboutText() { + return aboutText; + } + + /** + * Returns the application name or null. + * Note this is never shown to the user. + * It is used to initialize the SWT Display. + *

+ * On Motif, for example, this can be used + * to set the name used for resource lookup. + *

+ * + * @return the application name, or null + */ + public String getAppName() { + return appName; + } + + /** + * Returns the product name or null. + * This is shown in the window title and the About action. + * + * @return the product name, or null + */ + public String getProductName() { + return featurePluginLabel; + } + + /** + * Returns the provider name or null. + * + * @return the provider name, or null + */ + public String getProviderName() { + return providerName; + } + + /** + * Returns the feature version id. + * + * @return the version id of the feature + */ + public String getVersionId() { + return versionId; + } + + /** + * Returns a URL for the welcome page. + * Products designed to run "headless" typically would not have such an page. + * + * @return the welcome page, or null if none + */ + public URL getWelcomePageURL() { + return welcomePageURL; + } + + /** + * Returns the ID of a perspective in which to show the welcome page. + * May be null. + * + * @return the welcome page perspective id, or null if none + */ + public String getWelcomePerspectiveId() { + return welcomePerspective; + } + + /** + * Returns a String for the tips and trick href. + * + * @return the tips and tricks href, or null if none + */ + public String getTipsAndTricksHref() { + return tipsAndTricksHref; + } + + /** + * Returns the image url for the window image to use for this product. + * Products designed to run "headless" typically would not have such an image. + * + * @return the image url for the window image, or null if none + */ + public URL getWindowImageURL() { + return windowImageURL; + } + + /** + * Return an array of image URLs for the window images to use for + * this product. The expectations is that the elements will be the same + * image rendered at different sizes. Products designed to run "headless" + * typically would not have such images. + * + * @return an array of the image descriptors for the window images, or + * null if none + * @since 3.0 + */ + public URL[] getWindowImagesURLs() { + return windowImagesURLs; + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroup.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroup.java new file mode 100644 index 00000000000..bd8fcc7c985 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroup.java @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.IBundleGroup; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Platform; +import org.osgi.framework.Bundle; + +/** + * A bundle group backed by an installed feature. The feature identity comes + * from platform.xml, everything else is read from the feature and its branding + * bundle on demand. + */ +public class FeatureBundleGroup implements IBundleGroup, IBundleGroupConstants, IProductConstants { + + private static final String FEATURE_XML = "feature.xml"; //$NON-NLS-1$ + private static final String FEATURE_PROPERTIES = "feature"; //$NON-NLS-1$ + private static final String KEY_PREFIX = "%"; //$NON-NLS-1$ + private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$ + + private final String id; + private final String version; + private final String pluginIdentifier; + private final String pluginVersion; + private final URI featureLocation; + + private AboutInfo branding; + private FeatureManifest manifest; + private boolean fullyParsed; + private ResourceBundle resourceBundle; + private boolean resourceBundleLoaded; + + FeatureBundleGroup(String id, String version, String pluginIdentifier, String pluginVersion, URI featureLocation) { + this.id = id; + this.version = version; + this.pluginIdentifier = pluginIdentifier; + this.pluginVersion = pluginVersion; + this.featureLocation = featureLocation; + } + + /** + * A feature contributes a bundle group only when its branding bundle is + * installed. + */ + boolean hasBranding() { + String bundleId = getFeaturePluginIdentifier(); + return bundleId != null && Platform.getBundle(bundleId) != null; + } + + private String getFeaturePluginIdentifier() { + return pluginIdentifier != null && !pluginIdentifier.isEmpty() ? pluginIdentifier : id; + } + + @Override + public String getIdentifier() { + return id; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getName() { + return getBranding().getProductName(); + } + + @Override + public String getProviderName() { + return getBranding().getProviderName(); + } + + @Override + public String getDescription() { + FeatureManifest parsed = fullParse(); + return parsed == null ? null : getResourceString(parsed.description); + } + + @Override + public Bundle[] getBundles() { + FeatureManifest parsed = fullParse(); + if (parsed == null) { + return new Bundle[0]; + } + List bundles = new ArrayList<>(parsed.pluginIds.size()); + for (String pluginId : parsed.pluginIds) { + Bundle bundle = Platform.getBundle(pluginId); + if (bundle != null) { + bundles.add(bundle); + } + } + return bundles.toArray(new Bundle[bundles.size()]); + } + + @Override + public String getProperty(String key) { + if (key == null) { + return null; + } + AboutInfo info = getBranding(); + + // IBundleGroupConstants + if (key.equals(FEATURE_IMAGE)) { + return toExternalForm(info.getFeatureImageURL()); + } else if (key.equals(TIPS_AND_TRICKS_HREF)) { + return info.getTipsAndTricksHref(); + } else if (key.equals(IBundleGroupConstants.WELCOME_PAGE)) { // same value is used by product and bundle group + return toExternalForm(info.getWelcomePageURL()); + } else if (key.equals(WELCOME_PERSPECTIVE)) { + return info.getWelcomePerspectiveId(); + } else if (key.equals(BRANDING_BUNDLE_ID)) { + return pluginIdentifier; + } else if (key.equals(BRANDING_BUNDLE_VERSION)) { + return pluginVersion; + } else if (key.equals(APP_NAME)) { + return info.getAppName(); + } else if (key.equals(ABOUT_TEXT)) { + return info.getAboutText(); + } else if (key.equals(ABOUT_IMAGE)) { + return toExternalForm(info.getAboutImageURL()); + } else if (key.equals(WINDOW_IMAGE)) { + return toExternalForm(info.getWindowImageURL()); + } else if (key.equals(WINDOW_IMAGES)) { + URL[] urls = info.getWindowImagesURLs(); + if (urls == null) { + return null; + } + StringBuilder windowImagesURLs = new StringBuilder(); + for (int i = 0; i < urls.length; i++) { + windowImagesURLs.append(urls[i].toExternalForm()); + if (i != urls.length - 1) { + windowImagesURLs.append(','); + } + } + return windowImagesURLs.toString(); + } else if (key.equals(LICENSE_HREF)) { + return getLicenseURL(); + } + + return null; + } + + private static String toExternalForm(URL url) { + return url == null ? null : url.toExternalForm(); + } + + private synchronized AboutInfo getBranding() { + if (branding == null) { + branding = AboutInfo.readFeatureInfo(id, version, getFeaturePluginIdentifier()); + } + return branding; + } + + private String getLicenseURL() { + FeatureManifest parsed = fullParse(); + if (parsed == null || parsed.licenseURL == null) { + return null; + } + String resolved = getResourceString(parsed.licenseURL); + if (resolved.startsWith("http://") || resolved.startsWith("https://")) { //$NON-NLS-1$ //$NON-NLS-2$ + return resolved; + } + return featureLocation.resolve(resolved).toString(); + } + + private synchronized FeatureManifest fullParse() { + if (!fullyParsed) { + fullyParsed = true; + try { + manifest = FeatureXmlParser.parse(featureLocation.resolve(FEATURE_XML).toURL(), false); + } catch (MalformedURLException | IllegalArgumentException e) { + ILog.of(FeatureBundleGroup.class).error("Cannot locate the feature.xml of " + id, e); //$NON-NLS-1$ + } + } + return manifest; + } + + /* + * Resolves a %key reference against the feature.properties of this feature. + */ + private String getResourceString(String value) { + if (value == null) { + return null; + } + String s = value.trim(); + if (s.isEmpty() || !s.startsWith(KEY_PREFIX)) { + return value; + } + if (s.startsWith(KEY_DOUBLE_PREFIX)) { + return s.substring(1); + } + + int index = s.indexOf(' '); + String key = index == -1 ? s : s.substring(0, index); + String defaultValue = index == -1 ? s : s.substring(index + 1); + + ResourceBundle bundle = getResourceBundle(); + if (bundle == null) { + return defaultValue; + } + try { + return bundle.getString(key.substring(1)); + } catch (MissingResourceException e) { + return defaultValue; + } + } + + private synchronized ResourceBundle getResourceBundle() { + if (!resourceBundleLoaded) { + resourceBundleLoaded = true; + try { + ClassLoader loader = new URLClassLoader(new URL[] { featureLocation.toURL() }, null); + resourceBundle = ResourceBundle.getBundle(FEATURE_PROPERTIES, getDefaultLocale(), loader); + } catch (MissingResourceException | MalformedURLException | IllegalArgumentException e) { + // features are not required to be translated + } + } + return resourceBundle; + } + + private static Locale getDefaultLocale() { + String nl = Platform.getNL(); + if (nl == null) { + return Locale.getDefault(); + } + StringTokenizer locales = new StringTokenizer(nl, "_"); //$NON-NLS-1$ + switch (locales.countTokens()) { + case 1: + return Locale.of(locales.nextToken()); + case 2: + return Locale.of(locales.nextToken(), locales.nextToken()); + case 3: + return Locale.of(locales.nextToken(), locales.nextToken(), locales.nextToken()); + default: + return Locale.getDefault(); + } + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroupProvider.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroupProvider.java new file mode 100644 index 00000000000..c4f6fec1577 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureBundleGroupProvider.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.io.File; +import java.io.FileFilter; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.internal.runtime.bundlegroups.platformxml.Configuration; +import org.eclipse.core.internal.runtime.bundlegroups.platformxml.ConfigurationParser; +import org.eclipse.core.internal.runtime.bundlegroups.platformxml.Feature; +import org.eclipse.core.internal.runtime.bundlegroups.platformxml.Site; +import org.eclipse.core.runtime.IBundleGroup; +import org.eclipse.core.runtime.IBundleGroupProvider; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.service.datalocation.Location; +import org.osgi.service.component.annotations.Component; + +/** + * Contributes the installed features as bundle groups. The feature list comes + * from the platform.xml written at provisioning time, so no plug-in has to be + * inspected to build it. + */ +@Component(service = IBundleGroupProvider.class) +public class FeatureBundleGroupProvider implements IBundleGroupProvider { + + private static final String PLATFORM_XML = "org.eclipse.update/platform.xml"; //$NON-NLS-1$ + private static final String FEATURES = "features"; //$NON-NLS-1$ + private static final String FEATURE_XML = "feature.xml"; //$NON-NLS-1$ + + private List cachedFeatures; + private long cachedTimestamp = -1; + + @Override + public String getName() { + return Messages.BundleGroupProvider; + } + + @Override + public synchronized IBundleGroup[] getBundleGroups() { + File platformXml = findPlatformXml(); + long timestamp = platformXml == null ? 0 : platformXml.lastModified(); + if (cachedFeatures == null || timestamp != cachedTimestamp) { + cachedFeatures = readFeatures(platformXml); + cachedTimestamp = timestamp; + } + // the branding bundle of a feature can be installed after the feature was read + return cachedFeatures.stream().filter(FeatureBundleGroup::hasBranding).toArray(IBundleGroup[]::new); + } + + private static List readFeatures(File platformXml) { + URL installArea = getInstallArea(); + List features = new ArrayList<>(); + Configuration configuration = platformXml == null ? null + : ConfigurationParser.parse(platformXml, installArea); + if (configuration == null) { + collectFromInstallArea(installArea, features); + } else { + for (Site site : configuration.getSites()) { + collectFromSite(site, features); + } + } + return features; + } + + private static void collectFromSite(Site site, List features) { + URI siteLocation = toURI(site.getUrl()); + if (siteLocation == null) { + return; + } + for (Feature feature : site.getFeatures()) { + if (feature.getId() == null) { + continue; + } + String featureURL = feature.getUrl(); + if (featureURL == null) { + featureURL = FEATURES + "/" + feature.getId() + "_" + feature.getVersion() + "/"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + features.add(new FeatureBundleGroup(feature.getId(), feature.getVersion(), feature.getPluginIdentifier(), + feature.getPluginVersion(), siteLocation.resolve(featureURL))); + } + } + + /* + * Without a platform.xml the installed features can only be found by looking at + * the features directory of the install area. + */ + private static void collectFromInstallArea(URL installArea, List features) { + if (installArea == null || !"file".equals(installArea.getProtocol())) { //$NON-NLS-1$ + return; + } + File featuresDir = new File(installArea.getFile().replace('/', File.separatorChar), FEATURES); + File[] dirs = featuresDir + .listFiles((FileFilter) f -> f.isDirectory() && new File(f, FEATURE_XML).exists()); + if (dirs == null) { + return; + } + for (File dir : dirs) { + try { + FeatureManifest manifest = FeatureXmlParser.parse(new File(dir, FEATURE_XML).toURI().toURL(), true); + if (manifest != null) { + features.add(new FeatureBundleGroup(manifest.id, manifest.version, manifest.pluginIdentifier, + manifest.version, dir.toURI())); + } + } catch (MalformedURLException e) { + ILog.of(FeatureBundleGroupProvider.class).error("Cannot read the feature in " + dir, e); //$NON-NLS-1$ + } + } + } + + /* + * The platform.xml of the configuration area, falling back to the one of a + * shared install. + */ + private static File findPlatformXml() { + Location configuration = Platform.getConfigurationLocation(); + if (configuration == null) { + return null; + } + File file = toPlatformXml(configuration); + if (file != null && file.exists()) { + return file; + } + Location parent = configuration.getParentLocation(); + File parentFile = parent == null ? null : toPlatformXml(parent); + return parentFile != null && parentFile.exists() ? parentFile : null; + } + + private static File toPlatformXml(Location location) { + URL url = location.getURL(); + if (url == null || !"file".equals(url.getProtocol())) { //$NON-NLS-1$ + return null; + } + return new File(url.getFile().replace('/', File.separatorChar), PLATFORM_XML); + } + + private static URL getInstallArea() { + Location install = Platform.getInstallLocation(); + return install == null ? null : install.getURL(); + } + + private static URI toURI(String encodedURI) { + if (encodedURI == null) { + return null; + } + try { + return new URI(encodedURI); + } catch (URISyntaxException e) { + ILog.of(FeatureBundleGroupProvider.class).error("Cannot resolve the site " + encodedURI, e); //$NON-NLS-1$ + return null; + } + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureManifest.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureManifest.java new file mode 100644 index 00000000000..9c8a52faa72 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureManifest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.util.ArrayList; +import java.util.List; + +/** + * The contents of a feature.xml file, as far as a bundle group needs them. + */ +class FeatureManifest { + + String id; + String version; + String pluginIdentifier; + String application; + boolean primary; + String description; + String licenseURL; + final List pluginIds = new ArrayList<>(); +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureXmlParser.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureXmlParser.java new file mode 100644 index 00000000000..4a8f2cd27bb --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/FeatureXmlParser.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.StringTokenizer; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.util.NLS; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Parses a feature.xml file. In header mode parsing stops after the feature + * element, which is all the platform.xml fallback needs. + */ +class FeatureXmlParser extends DefaultHandler { + + private static final SAXException STOP_PARSING = new SAXException(""); //$NON-NLS-1$ + + private final boolean headerOnly; + private final FeatureManifest manifest = new FeatureManifest(); + private final StringBuilder description = new StringBuilder(); + private boolean isDescription; + + static FeatureManifest parse(URL featureXML, boolean headerOnly) { + FeatureXmlParser handler = new FeatureXmlParser(headerOnly); + SAXParser parser; + try { + parser = createParser(); + } catch (ParserConfigurationException | SAXException e) { + ILog.of(FeatureXmlParser.class).error("Failed to create a parser for " + featureXML, e); //$NON-NLS-1$ + return null; + } + try (InputStream in = featureXML.openStream()) { + parser.parse(new InputSource(in), handler); + } catch (SAXException e) { + if (e != STOP_PARSING) { + return handler.manifest.id == null ? null : handler.manifest; + } + } catch (IOException e) { + return null; + } + return handler.manifest.id == null ? null : handler.manifest; + } + + @SuppressWarnings("restriction") + private static SAXParser createParser() throws ParserConfigurationException, SAXException { + return org.eclipse.core.internal.runtime.XmlProcessorFactory.createSAXParserWithErrorOnDOCTYPE(true); + } + + private FeatureXmlParser(boolean headerOnly) { + this.headerOnly = headerOnly; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if ("feature".equals(localName)) { //$NON-NLS-1$ + processFeature(attributes); + if (headerOnly) { + throw STOP_PARSING; + } + } else if ("plugin".equals(localName)) { //$NON-NLS-1$ + processPlugin(attributes); + } else if ("description".equals(localName)) { //$NON-NLS-1$ + isDescription = true; + } else if ("license".equals(localName)) { //$NON-NLS-1$ + manifest.licenseURL = attributes.getValue("url"); //$NON-NLS-1$ + } + } + + private void processFeature(Attributes attributes) { + String id = attributes.getValue("id"); //$NON-NLS-1$ + String version = attributes.getValue("version"); //$NON-NLS-1$ + if (id == null || id.trim().isEmpty() || version == null || version.trim().isEmpty()) { + ILog.of(FeatureXmlParser.class).warn(NLS.bind(Messages.FeatureParser_IdOrVersionInvalid, id, version)); + return; + } + if (!isValidEnvironment(attributes)) { + return; + } + manifest.id = id; + manifest.version = version; + manifest.pluginIdentifier = attributes.getValue("plugin"); //$NON-NLS-1$ + manifest.application = attributes.getValue("application"); //$NON-NLS-1$ + manifest.primary = "true".equals(attributes.getValue("primary")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private void processPlugin(Attributes attributes) { + String id = attributes.getValue("id"); //$NON-NLS-1$ + String version = attributes.getValue("version"); //$NON-NLS-1$ + if (id == null || id.trim().isEmpty() || version == null || version.trim().isEmpty()) { + ILog.of(FeatureXmlParser.class).warn(NLS.bind(Messages.FeatureParser_IdOrVersionInvalid, id, version)); + return; + } + if (!isValidEnvironment(attributes)) { + return; + } + manifest.pluginIds.add(id); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (isDescription) { + description.append(ch, start, length); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if ("description".equals(localName)) { //$NON-NLS-1$ + isDescription = false; + manifest.description = description.toString().trim(); + } + } + + private static boolean isValidEnvironment(Attributes attributes) { + String os = attributes.getValue("os"); //$NON-NLS-1$ + String ws = attributes.getValue("ws"); //$NON-NLS-1$ + String arch = attributes.getValue("arch"); //$NON-NLS-1$ + String nl = attributes.getValue("nl"); //$NON-NLS-1$ + if (os != null && !isMatching(os, Platform.getOS())) { + return false; + } + if (ws != null && !isMatching(ws, Platform.getWS())) { + return false; + } + if (arch != null && !isMatching(arch, Platform.getOSArch())) { + return false; + } + if (nl != null && !isMatchingLocale(nl, Platform.getNL())) { + return false; + } + return true; + } + + private static boolean isMatching(String candidateValues, String siteValues) { + if (siteValues == null) { + return false; + } + if ("*".equalsIgnoreCase(candidateValues)) { //$NON-NLS-1$ + return true; + } + siteValues = siteValues.toUpperCase(); + StringTokenizer stok = new StringTokenizer(candidateValues, ","); //$NON-NLS-1$ + while (stok.hasMoreTokens()) { + String token = stok.nextToken().toUpperCase(); + if (siteValues.contains(token)) { + return true; + } + } + return false; + } + + private static boolean isMatchingLocale(String candidateValues, String locale) { + if (locale == null) { + return false; + } + if ("*".equalsIgnoreCase(candidateValues)) { //$NON-NLS-1$ + return true; + } + locale = locale.toUpperCase(); + candidateValues = candidateValues.toUpperCase(); + StringTokenizer stok = new StringTokenizer(candidateValues, ","); //$NON-NLS-1$ + while (stok.hasMoreTokens()) { + String candidate = stok.nextToken(); + if (locale.indexOf(candidate) == 0) { + return true; + } + if (candidate.indexOf(locale) == 0) { + return true; + } + } + return false; + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IBundleGroupConstants.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IBundleGroupConstants.java new file mode 100644 index 00000000000..bcedef3c09c --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IBundleGroupConstants.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2004, 2025 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + + +/** + * These constants define the set of properties that the UI expects to + * be available via IBundleGroup.getProperty(String). + * + * @since 3.0 + * @see org.eclipse.core.runtime.IBundleGroup#getProperty(String) + */ +public interface IBundleGroupConstants { + /** + * An image which can be shown in an "about features" dialog (32x32). + */ + public static final String FEATURE_IMAGE = "featureImage"; //$NON-NLS-1$ + + /** + * A help reference for the feature's tips and tricks page (optional). + */ + public static final String TIPS_AND_TRICKS_HREF = "tipsAndTricksHref"; //$NON-NLS-1$ + + /** + * A URL for the feature's welcome page (special XML-based format) + * ($nl$/ prefix to permit locale-specific translations of entire file). + * Products designed to run "headless" typically would not have such a page. + */ + public static final String WELCOME_PAGE = "welcomePage"; //$NON-NLS-1$ + + /** + * The id of a perspective in which to show the welcome page + * (optional). + */ + public static final String WELCOME_PERSPECTIVE = "welcomePerspective"; //$NON-NLS-1$ + + /** + * The URL of the license page for the feature + * (optional). + */ + public static final String LICENSE_HREF = "licenseHref"; //$NON-NLS-1$ + + /** + * The id of the bundle group's branding bundle. + * + * @since 3.3 + */ + public static final String BRANDING_BUNDLE_ID= "brandingBundleId"; //$NON-NLS-1$ + + /** + * The version of the bundle group's branding bundle. + * + * @since 3.3 + */ + public static final String BRANDING_BUNDLE_VERSION= "brandingBundleVersion"; //$NON-NLS-1$ +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IProductConstants.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IProductConstants.java new file mode 100644 index 00000000000..7111b183518 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IProductConstants.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +/** + * These constants define the set of properties that the UI expects to + * be available via IProduct.getProperty(String). + * + * @since 3.0 + * @see org.eclipse.core.runtime.IProduct#getProperty(String) + */ +public interface IProductConstants { + /** + * The application name, used to initialize the SWT Display. This + * value is distinct from the string displayed in the application + * title bar. + *

+ * E.g., On motif, this can be used to set the name used for + * resource lookup. + *

+ */ + public static final String APP_NAME = "appName"; //$NON-NLS-1$ + + /** + * The text to show in an "about" dialog for this product. + * Products designed to run "headless" typically would not + * have such text. + */ + public static final String ABOUT_TEXT = "aboutText"; //$NON-NLS-1$ + + /** + * An image which can be shown in an "about" dialog for this + * product. Products designed to run "headless" typically would not + * have such an image. + *

+ * A full-sized product image (no larger than 500x330 pixels) is + * shown without the "aboutText" blurb. A half-sized product image + * (no larger than 250x330 pixels) is shown with the "aboutText" + * blurb beside it. + */ + public static final String ABOUT_IMAGE = "aboutImage"; //$NON-NLS-1$ + + /** + * An image to be used as the window icon for this product (16x16). + * Products designed to run "headless" typically would not have such an image. + *

+ * If the WINDOW_IMAGES property is given, then it supercedes + * this one. + *

+ */ + public static final String WINDOW_IMAGE = "windowImage"; //$NON-NLS-1$ + + /** + * An array of one or more images to be used for this product. The + * expectation is that the array will contain the same image rendered + * at different sizes (16x16 and 32x32). + * Products designed to run "headless" typically would not have such images. + *

+ * If this property is given, then it supercedes WINDOW_IMAGE. + *

+ */ + public static final String WINDOW_IMAGES = "windowImages"; //$NON-NLS-1$ + /** + * A URL for the products's welcome page (special XML-based format) + * ($nl$/ prefix to permit locale-specific translations of entire file). + * Products designed to run "headless" typically would not have such a page. + */ + public static final String WELCOME_PAGE = "welcomePage"; //$NON-NLS-1$ +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IniFileReader.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IniFileReader.java new file mode 100644 index 00000000000..f4d48bef45d --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/IniFileReader.java @@ -0,0 +1,389 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.text.MessageFormat; // Can't use ICU, possible launch problem? +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.PropertyResourceBundle; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; + +/** + * Reads the information found in an "INI" file. This file must be in a + * standard Java properties format. A properties file may also be provided + * to NL values in the INI file - values must start with the % prefix. A + * mapping file may also be provided that contains "fill-ins" for the + * properties file - format being "n = some text", where n is a number. + */ +public class IniFileReader { + private static final String PID = "org.eclipse.core.runtime.bundlegroups"; //$NON-NLS-1$ + private static final Status OK_STATUS = new Status(IStatus.OK, PID, 0, "", null); //$NON-NLS-1$ + private static final String KEY_PREFIX = "%"; //$NON-NLS-1$ + private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$ + private static final String NLS_TAG = "$nl$"; //$NON-NLS-1$ + + private final String featureId; + private final String pluginId; + private final String iniFilename; + private final String propertiesFilename; + private final String mappingsFilename; + private Properties ini = null; + private PropertyResourceBundle properties = null; + private Object[] mappings = null; + private Bundle bundle; + + /** + * Creates an INI file reader that can parse the contents into key,value pairs. + * + * @param featureId the unique identifier of the feature, must not be null + * @param pluginId the unique identifier of the feature plug-in, must not be null + * @param iniFilename the INI file name, must not be null + * @param propertiesFilename the properties filename, can be null if not required + * @param mappingsFilename the mappings filename, can be null if not required + */ + public IniFileReader(String featureId, String pluginId, String iniFilename, String propertiesFilename, String mappingsFilename) { + super(); + + if (featureId == null || pluginId == null || iniFilename == null) { + throw new IllegalArgumentException(); + } + + this.featureId = featureId; + this.pluginId = pluginId; + this.iniFilename = iniFilename; + this.propertiesFilename = propertiesFilename; + this.mappingsFilename = mappingsFilename; + } + + /** + * Read the contents of the INI, properties, and mappings files. + * Does nothing if the content has already been read and parsed. + * + * @return an IStatus indicating the success or failure + * of reading and parsing the INI file content + */ + public IStatus load() { + if (ini != null) { + return OK_STATUS; + } + + // attempt to locate the corresponding plugin + bundle = Platform.getBundle(pluginId); + if (bundle == null || bundle.getState() == Bundle.UNINSTALLED || bundle.getState() == Bundle.INSTALLED) { + bundle = null; // make it null for other test down the road + String message = NLS.bind(Messages.IniFileReader_MissingDesc, featureId); + return new Status(IStatus.ERROR, PID, 0, message, null); + } + + // Determine the ini file location + URL iniURL = null; + IOException ioe = null; + iniURL = FileLocator.find(bundle, IPath.fromOSString(NLS_TAG).append(iniFilename), null); + if (iniURL == null) { + String message = NLS.bind(Messages.IniFileReader_OpenINIError, iniFilename); + return new Status(IStatus.ERROR, PID, 0, message, ioe); + } + + // Determine the properties file location + URL propertiesURL = null; + if (propertiesFilename != null && !propertiesFilename.isEmpty()) { + propertiesURL = FileLocator.find(bundle, IPath.fromOSString(NLS_TAG).append(propertiesFilename), null); + } + + // Determine the mappings file location + URL mappingsURL = null; + if (mappingsFilename != null && mappingsFilename.length() > 0) { + mappingsURL = FileLocator.find(bundle, IPath.fromOSString(NLS_TAG).append(mappingsFilename), null); + } + + // OK to pass null properties and/or mapping file + return load(iniURL, propertiesURL, mappingsURL); + } + + /** + * Returns the string value for the given key, or null. + * The string value is NLS if requested. + * + * @return the string value for the given key, or null + */ + public String getString(String key, boolean doNls, Hashtable runtimeMappings) { + if (ini == null) { + return null; + } + String value = ini.getProperty(key); + if (value != null && doNls) { + return getResourceString(value, runtimeMappings); + } + return value; + } + + + /** + * Returns a URL for the given key, or null. + * + * @return a URL for the given key, or null + */ + public URL getURL(String key) { + if (ini == null) { + return null; + } + + URL url = null; + String fileName = ini.getProperty(key); + if (fileName != null) { + if (bundle == null) { + return null; + } + url = FileLocator.find(bundle, IPath.fromOSString(fileName), null); + } + return url; + } + + /** + * Returns a array of URL for the given key, or null. The + * property value should be a comma separated list of urls, tokens for + * which bundle cannot build an url will have a null entry. + * + * @param key name of the property containing the requested urls + * @return a URL for the given key, or null + * @since 3.0 + */ + public URL[] getURLs(String key) { + if (ini == null || bundle == null) { + return null; + } + + String value = ini.getProperty(key); + if (value == null) { + return null; + } + + StringTokenizer tokens = new StringTokenizer(value, ","); //$NON-NLS-1$ + ArrayList array = new ArrayList<>(10); + while (tokens.hasMoreTokens()) { + String str = tokens.nextToken().trim(); + array.add(FileLocator.find(bundle, IPath.fromOSString(str), null)); + } + + URL[] urls = new URL[array.size()]; + array.toArray(urls); + return urls; + } + + /** + * Returns the feature plugin label, or null. + * + * @return the feature plugin label, or null if none. + */ + public String getFeaturePluginLabel() { + if (bundle == null) { + return null; + } + return bundle.getHeaders().get(Constants.BUNDLE_NAME); + } + + /** + * Returns the provider name for this feature, or null. + * + * @return the provider name for this feature, or null + */ + public String getProviderName() { + if (bundle == null) { + return null; + } + return bundle.getHeaders().get(Constants.BUNDLE_VENDOR); + } + + /* + * Returns a resource string corresponding to the given argument + * value and bundle. + * If the argument value specifies a resource key, the string + * is looked up in the given resource bundle. If the argument does not + * specify a valid key, the argument itself is returned as the + * resource string. The key lookup is performed against the + * specified resource bundle. If a resource string + * corresponding to the key is not found in the resource bundle + * the key value, or any default text following the key in the + * argument value is returned as the resource string. + * A key is identified as a string begining with the "%" character. + * Note that the "%" character is stripped off prior to lookup + * in the resource bundle. + *

+ * For example, assume resource bundle plugin.properties contains + * name = Project Name + *

+	 *     
  • getResourceString("Hello World") returns "Hello World"
  • + *
  • getResourceString("%name") returns "Project Name"
  • + *
  • getResourceString("%name Hello World") returns "Project Name"
  • + *
  • getResourceString("%abcd Hello World") returns "Hello World"
  • + *
  • getResourceString("%abcd") returns "%abcd"
  • + *
  • getResourceString("%%name") returns "%name"
  • + *
  • getResourceString(null) returns null
  • + *
    + *

    + * + * @param value the value or null + * @param runtimeMappings runtime mappings or null + * @return the resource string + */ + public String getResourceString(String value, Hashtable runtimeMappings) { + + if (value == null) { + return null; + } + String s = value.trim(); + + if (!s.startsWith(KEY_PREFIX)) { + return s; + } + + if (s.startsWith(KEY_DOUBLE_PREFIX)) { + return s.substring(1); + } + + int ix = s.indexOf(" "); //$NON-NLS-1$ + String key = ix == -1 ? s : s.substring(0, ix); + String dflt = ix == -1 ? s : s.substring(ix + 1); + + if (properties == null) { + return dflt; + } + + String result = null; + try { + result = properties.getString(key.substring(1)); + } catch (MissingResourceException e) { + return dflt; + } + if (runtimeMappings != null) { + for (Enumeration e = runtimeMappings.keys(); e.hasMoreElements();) { + String keyValue = e.nextElement(); + int i = result.indexOf(keyValue); + if (i != -1) { + String s1 = result.substring(0,i); + String s2 = runtimeMappings.get(keyValue); + String s3 = result.substring(i+keyValue.length()); + result = s1 + s2 + s3; + } + } + } + + if (result.indexOf('{') != -1) { + // We test for the curly braces since due to NL issues we do not + // want to use MessageFormat unless we have to. + try { + result = MessageFormat.format(result, mappings); + } catch (IllegalArgumentException e) { + //ignore and return string without bound parameters + } + } + + return result; + } + + /* + * Read the contents of the ini, properties, and mappings files. + */ + private IStatus load(URL iniURL, URL propertiesURL, URL mappingsURL) { + + InputStream is = null; + try { + is = iniURL.openStream(); + ini = new Properties(); + ini.load(is); + } catch (IOException e) { + ini = null; + String message = NLS.bind(Messages.IniFileReader_ReadIniError, iniURL.toExternalForm()); + return new Status(IStatus.ERROR, PID, 0, message, e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + } + } + + if (propertiesURL != null) { + is = null; + try { + is = propertiesURL.openStream(); + properties = new PropertyResourceBundle(is); + } catch (IOException e) { + properties = null; + String message = NLS.bind(Messages.IniFileReader_ReadPropError, propertiesURL.toExternalForm()); + return new Status(IStatus.ERROR, PID, 0, message, e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + } + } + } + + PropertyResourceBundle bundle = null; + if (mappingsURL != null) { + is = null; + try { + is = mappingsURL.openStream(); + bundle = new PropertyResourceBundle(is); + } catch (IOException e) { + bundle = null; + String message = NLS.bind(Messages.IniFileReader_ReadMapError, mappingsURL.toExternalForm()); + return new Status(IStatus.ERROR, PID, 0, message, e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + } + } + } + + ArrayList mappingsList = new ArrayList<>(); + if (bundle != null) { + boolean found = true; + int i = 0; + while (found) { + try { + mappingsList.add(bundle.getString(Integer.toString(i))); + } catch (MissingResourceException e) { + found = false; + } + i++; + } + } + mappings = mappingsList.toArray(new String[mappingsList.size()]); + + return OK_STATUS; + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/Messages.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/Messages.java new file mode 100644 index 00000000000..3c08954706e --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/Messages.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups; + +import org.eclipse.osgi.util.NLS; + +public final class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.core.internal.runtime.bundlegroups.messages";//$NON-NLS-1$ + + private Messages() { + // Do not instantiate + } + + public static String BundleGroupProvider; + public static String FeatureParser_IdOrVersionInvalid; + public static String IniFileReader_MissingDesc; + public static String IniFileReader_OpenINIError; + public static String IniFileReader_ReadIniError; + public static String IniFileReader_ReadPropError; + public static String IniFileReader_ReadMapError; + + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/messages.properties b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/messages.properties new file mode 100644 index 00000000000..67242172d1b --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/messages.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2000, 2008 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +BundleGroupProvider=Installed Features + +FeatureParser_IdOrVersionInvalid= Error parsing feature stream. The unique identifier or the version is null or empty: unique identifier=\"{0}\" version=\"{1}\". + +IniFileReader_MissingDesc = Cannot find plugin for feature {0} +IniFileReader_OpenINIError = Cannot open branding file {0} +IniFileReader_ReadIniError = Error reading branding file {0} +IniFileReader_ReadPropError = Error reading branding properties file {0} +IniFileReader_ReadMapError = Error reading mapping files {0} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Configuration.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Configuration.java new file mode 100644 index 00000000000..5753448e72c --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Configuration.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups.platformxml; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.URIUtil; + +/** + * The contents of a platform.xml file. + */ +public class Configuration { + + private final List sites = new ArrayList<>(); + private String sharedUR; + private URL osgiInstallArea; + + void setSharedUR(String value) { + sharedUR = value; + } + + void setOsgiInstallArea(URL osgiInstallArea) { + this.osgiInstallArea = osgiInstallArea; + } + + /** + * The sites of this configuration, followed by those of the shared + * configuration it links to, if any. + */ + public List getSites() { + if (sharedUR == null) { + return sites; + } + List result = new ArrayList<>(sites); + Configuration parent = loadParent(); + if (parent != null) { + result.addAll(parent.getSites()); + } + return result; + } + + private Configuration loadParent() { + File location; + try { + location = URIUtil.toFile(new URI(sharedUR)); + } catch (URISyntaxException e) { + return null; + } + if (location == null) { + return null; + } + if (!location.isAbsolute()) { + File installArea = toFile(osgiInstallArea); + if (installArea == null) { + return null; + } + location = new File(installArea, location.getPath()); + } + return ConfigurationParser.parse(location, osgiInstallArea); + } + + private static File toFile(URL url) { + try { + return url == null ? null : URIUtil.toFile(URIUtil.toURI(url)); + } catch (URISyntaxException e) { + return null; + } + } + + void add(Site site) { + sites.add(site); + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationConstants.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationConstants.java new file mode 100644 index 00000000000..d241a1c4fd2 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationConstants.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups.platformxml; + +/** + * Element and attribute names used in a platform.xml file. + */ +public interface ConfigurationConstants { + + String ELEMENT_CONFIG = "config"; //$NON-NLS-1$ + String ELEMENT_FEATURE = "feature"; //$NON-NLS-1$ + String ELEMENT_SITE = "site"; //$NON-NLS-1$ + + String ATTRIBUTE_ID = "id"; //$NON-NLS-1$ + String ATTRIBUTE_PLUGIN_IDENTIFIER = "plugin-identifier"; //$NON-NLS-1$ + String ATTRIBUTE_PLUGIN_VERSION = "plugin-version"; //$NON-NLS-1$ + String ATTRIBUTE_SHARED_UR = "shared_ur"; //$NON-NLS-1$ + String ATTRIBUTE_URL = "url"; //$NON-NLS-1$ + String ATTRIBUTE_VERSION = "version"; //$NON-NLS-1$ + +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationParser.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationParser.java new file mode 100644 index 00000000000..dc9cf4bf4d4 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/ConfigurationParser.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2007, 2015 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups.platformxml; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.URIUtil; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * Parser for platform.xml files. + */ +public class ConfigurationParser implements ConfigurationConstants { + static final String PLATFORM_BASE = "platform:/base/"; //$NON-NLS-1$ + private final URL osgiInstallArea; + + /* + * Parse the given file handle which points to a platform.xml file and return a + * configuration object. Returns null if the file doesn't exist or cannot be read. + */ + public static Configuration parse(File file, URL osgiInstallArea) { + return new ConfigurationParser(osgiInstallArea).internalParse(file); + } + + private ConfigurationParser(URL osgiInstallArea) { + this.osgiInstallArea = osgiInstallArea; + } + + /* + * Create a feature object based on the given DOM node. Return the new feature. + */ + private static Feature createFeature(Node node) { + Feature result = new Feature(); + String id = getAttribute(node, ATTRIBUTE_ID); + if (id != null) { + result.setId(id); + } + String url = getAttribute(node, ATTRIBUTE_URL); + if (url != null) { + result.setUrl(url); + } + String version = getAttribute(node, ATTRIBUTE_VERSION); + if (version != null) { + result.setVersion(version); + } + String pluginIdentifier = getAttribute(node, ATTRIBUTE_PLUGIN_IDENTIFIER); + if (pluginIdentifier != null) { + result.setPluginIdentifier(pluginIdentifier); + } + String pluginVersion = getAttribute(node, ATTRIBUTE_PLUGIN_VERSION); + // plug-in version is the same as the feature version if it is missing + if (pluginVersion == null) { + pluginVersion = version; + } + if (pluginVersion != null) { + result.setPluginVersion(pluginVersion); + } + return result; + } + + /* + * Create the features from the given DOM node. + */ + private static void createFeatures(Node node, Site site) { + NodeList children = node.getChildNodes(); + int size = children.getLength(); + for (int i = 0; i < size; i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + if (!ELEMENT_FEATURE.equalsIgnoreCase(child.getNodeName())) { + continue; + } + site.addFeature(createFeature(child)); + } + } + + /* + * Create a site based on the given DOM node. + */ + private Site createSite(Node node) { + Site result = new Site(); + String url = getAttribute(node, ATTRIBUTE_URL); + if (url != null) { + try { + // do this to ensure the location is an encoded URI + URI uri = URIUtil.fromString(url); + URI osgiURI = osgiInstallArea != null ? URIUtil.toURI(osgiInstallArea) : null; + result.setUrl(getLocation(uri, osgiURI).toString()); + } catch (URISyntaxException e) { + result.setUrl(url); + } + } + createFeatures(node, result); + return result; + } + + /* + * Convert the given url string to an absolute url. If the string is + * platform:/base/ then return a string which represents the osgi install area. + */ + private static URI getLocation(URI location, URI osgiArea) { + if (osgiArea == null) { + return location; + } + if (PLATFORM_BASE.equals(location.toString())) { + return osgiArea; + } + return URIUtil.makeAbsolute(location, osgiArea); + } + + /* + * Return the attribute with the given name, or null if it does not exist. + */ + private static String getAttribute(Node node, String name) { + NamedNodeMap attributes = node.getAttributes(); + Node temp = attributes.getNamedItem(name); + return temp == null ? null : temp.getNodeValue(); + } + + /* + * Load the given file into a DOM document. + */ + @SuppressWarnings("restriction") + private static Document load(InputStream input) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory factory = org.eclipse.core.internal.runtime.XmlProcessorFactory + .createDocumentBuilderFactoryWithErrorOnDOCTYPE(); + DocumentBuilder builder = factory.newDocumentBuilder(); + try (InputStream buffered = new BufferedInputStream(input)) { + return builder.parse(buffered); + } + } + + private Configuration internalParse(File file) { + if (!file.exists()) { + return null; + } + try (InputStream input = new FileInputStream(file)) { + Configuration result = process(load(input)); + if (result != null) { + result.setOsgiInstallArea(osgiInstallArea); + } + return result; + } catch (IOException | ParserConfigurationException | SAXException e) { + ILog.of(ConfigurationParser.class).error("Failed to read " + file, e); //$NON-NLS-1$ + return null; + } + } + + /* + * Process the given DOM document and create the appropriate site objects. + */ + private Configuration process(Document document) { + Node node = getConfigElement(document); + if (node == null) { + return null; + } + Configuration configuration = createConfiguration(node); + NodeList children = node.getChildNodes(); + int size = children.getLength(); + for (int i = 0; i < size; i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + if (!ELEMENT_SITE.equalsIgnoreCase(child.getNodeName())) { + continue; + } + Site site = createSite(child); + if (site != null) { + configuration.add(site); + } + } + return configuration; + } + + private static Configuration createConfiguration(Node node) { + Configuration result = new Configuration(); + String value = getAttribute(node, ATTRIBUTE_SHARED_UR); + if (value != null) { + result.setSharedUR(value); + } + return result; + } + + private static Node getConfigElement(Document doc) { + NodeList children = doc.getChildNodes(); + int size = children.getLength(); + for (int i = 0; i < size; i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + if (ELEMENT_CONFIG.equalsIgnoreCase(child.getNodeName())) { + return child; + } + } + return null; + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Feature.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Feature.java new file mode 100644 index 00000000000..222cf79a969 --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Feature.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups.platformxml; + +/* + * Represents a feature entry in a platform.xml file. + */ +public class Feature { + + private String id; + private String url; + private String version; + private String pluginIdentifier; + private String pluginVersion; + + public String getId() { + return id; + } + + public String getPluginIdentifier() { + return pluginIdentifier; + } + + public String getPluginVersion() { + return pluginVersion; + } + + public String getUrl() { + return url; + } + + public String getVersion() { + return version; + } + + public void setId(String id) { + this.id = id; + } + + public void setPluginIdentifier(String pluginIdentifier) { + this.pluginIdentifier = pluginIdentifier; + } + + public void setPluginVersion(String pluginVersion) { + this.pluginVersion = pluginVersion; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Site.java b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Site.java new file mode 100644 index 00000000000..8348e99a4dc --- /dev/null +++ b/runtime/bundles/org.eclipse.core.runtime.bundlegroups/src/org/eclipse/core/internal/runtime/bundlegroups/platformxml/Site.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.internal.runtime.bundlegroups.platformxml; + +import java.util.ArrayList; +import java.util.List; + +/* + * Represents a site in a platform.xml file. + */ +public class Site { + + private String url; + private final List features = new ArrayList<>(); + + public void addFeature(Feature feature) { + this.features.add(feature); + } + + public List getFeatures() { + return features; + } + + /** + * Note the string that we are returning is an ENCODED URI string. + */ + public String getUrl() { + return url; + } + + /** + * Note that the string should be an ENCODED URI string. + */ + public void setUrl(String url) { + this.url = url; + } +} diff --git a/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF b/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF index 9e92d1fd8ce..7217575c039 100644 --- a/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF +++ b/update/org.eclipse.update.configurator/META-INF/MANIFEST.MF @@ -17,5 +17,4 @@ Import-Package: javax.xml.parsers, org.xml.sax.helpers Bundle-ActivationPolicy: lazy Automatic-Module-Name: org.eclipse.update.configurator -Service-Component: OSGI-INF/org.eclipse.update.internal.configurator.BundleGroupComponent.xml, - OSGI-INF/org.eclipse.update.internal.configurator.PlatformConfigurationFactory.xml +Service-Component: OSGI-INF/org.eclipse.update.internal.configurator.PlatformConfigurationFactory.xml diff --git a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BundleGroupComponent.java b/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BundleGroupComponent.java deleted file mode 100644 index 113d20ce3ad..00000000000 --- a/update/org.eclipse.update.configurator/src/org/eclipse/update/internal/configurator/BundleGroupComponent.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2017 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.update.internal.configurator; - -import java.util.ArrayList; - -import org.eclipse.core.runtime.IBundleGroup; -import org.eclipse.core.runtime.IBundleGroupProvider; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; - -/** - * Declarative services component that provides an implementation of - * {@link IBundleGroupProvider}. This allows the bundle group provider to be - * made available in the service registry before this bundle has started. - */ -@Component(service = IBundleGroupProvider.class) -@SuppressWarnings("removal") -public class BundleGroupComponent implements IBundleGroupProvider { - - - private final org.eclipse.update.configurator.IPlatformConfigurationFactory factory; - - @Activate - public BundleGroupComponent(@Reference org.eclipse.update.configurator.IPlatformConfigurationFactory factory) { - this.factory = factory; - } - - @Override - public IBundleGroup[] getBundleGroups() { - org.eclipse.update.configurator.IPlatformConfiguration configuration = factory - .getCurrentPlatformConfiguration(); - if (configuration == null) { - return new IBundleGroup[0]; - } - org.eclipse.update.configurator.IPlatformConfiguration.IFeatureEntry[] features = configuration - .getConfiguredFeatureEntries(); - ArrayList bundleGroups = new ArrayList<>(features.length); - for (org.eclipse.update.configurator.IPlatformConfiguration.IFeatureEntry feature : features) { - if (feature instanceof FeatureEntry && ((FeatureEntry) feature).hasBranding()) { - bundleGroups.add((IBundleGroup) feature); - } - } - return bundleGroups.toArray(new IBundleGroup[bundleGroups.size()]); - } - - @Override - public String getName() { - return Messages.BundleGroupProvider; - } - -}