Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf')
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.classpath7
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.project28
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.core.prefs291
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.ui.prefs68
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/META-INF/MANIFEST.MF20
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/about.html28
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/build.properties10
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.properties14
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.xml19
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/pom.xml12
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/historyListeners.exsd91
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/operationApprovers.exsd78
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/CheckedOperationHistory.java352
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/EMFtoGMFCommandWrapper.java204
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/GMFtoEMFCommandWrapper.java183
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/ICommandWrapper.java184
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/INonDirtying.java26
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NestingNotifyingWorkspaceCommandStack.java231
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NotifyingWorkspaceCommandStack.java670
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandTreeIterator.java175
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandUtils.java291
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/GMFUnsafe.java314
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationHistoryDirtyState.java212
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationUtils.java139
-rw-r--r--plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/internal/gmf/Activator.java67
25 files changed, 3714 insertions, 0 deletions
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.classpath b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.classpath
new file mode 100644
index 00000000000..eca7bdba8f0
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.project b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.project
new file mode 100644
index 00000000000..d198355a356
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.papyrus.infra.emf.gmf</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.core.prefs b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..b3aa6d60f94
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,291 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=260
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=false
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=260
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=5
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.ui.prefs b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000000..954281dbc31
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,68 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_functional_interfaces=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.insert_inferred_type_arguments=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_redundant_type_arguments=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_anonymous_class_creation=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_lambda=true
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.use_type_arguments=false
+cleanup_profile=_Papyrus
+cleanup_settings_version=2
+eclipse.preferences.version=1
+formatter_profile=_Papyrus
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="false" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * Constructor.\n *\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*****************************************************************************\n * Copyright (c) ${year} CEA LIST and others.\n * \n * All rights reserved. This program and the accompanying materials\n * are made available under the terms of the Eclipse Public License v1.0\n * which accompanies this distribution, and is available at\n * http\://www.eclipse.org/legal/epl-v10.html\n *\n * Contributors\:\n * CEA LIST - Initial API and implementation\n * \n *****************************************************************************/\n</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * @author ${user}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/**\n * ${see_to_overridden}\n *\n * ${tags}\n */</template><template autoinsert\="false" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${see_to_target}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/META-INF/MANIFEST.MF b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..36c601e282b
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Export-Package: org.eclipse.papyrus.infra.emf.gmf.command,
+ org.eclipse.papyrus.infra.emf.gmf.util,
+ org.eclipse.papyrus.infra.emf.internal.gmf;x-internal:=true
+Require-Bundle: org.eclipse.papyrus.infra.core.log;bundle-version="1.2.0",
+ org.eclipse.papyrus.infra.tools;bundle-version="1.2.0",
+ org.eclipse.papyrus.infra.core;bundle-version="1.2.0",
+ org.eclipse.core.expressions;bundle-version="3.4.400",
+ org.eclipse.gmf.runtime.emf.type.core;bundle-version="1.7.0",
+ com.google.guava;bundle-version="11.0.0",
+ org.eclipse.papyrus.emf.facet.custom.core;bundle-version="1.2.0",
+ org.eclipse.papyrus.infra.core.sashwindows.di;bundle-version="1.2.0"
+Bundle-Vendor: %providerName
+Bundle-ActivationPolicy: lazy
+Bundle-Version: 1.2.0.qualifier
+Bundle-Name: %pluginName
+Bundle-Activator: org.eclipse.papyrus.infra.emf.internal.gmf.Activator
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.papyrus.infra.emf.gmf;singleton:=true
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/about.html b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/about.html
new file mode 100644
index 00000000000..dd3c089a94c
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>November 14, 2008</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/build.properties b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/build.properties
new file mode 100644
index 00000000000..525a89f2c33
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/build.properties
@@ -0,0 +1,10 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html,\
+ build.properties,\
+ plugin.xml,\
+ schema/
+src.includes = about.html,\
+ schema/
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.properties b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.properties
new file mode 100644
index 00000000000..a9f119d526c
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2016 Christian W. Damus and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Christian W. Damus - initial API and implementation
+#
+
+pluginName = Papyrus GMF Run-time Core
+providerName = Eclipse Modeling Project
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.xml b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.xml
new file mode 100644
index 00000000000..035a271ff6f
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/plugin.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (c) 2016 Christian W. Damus and others.
+
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Public License v1.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/legal/epl-v10.html
+
+ Contributors:
+ Christian W. Damus - Initial API and implementation
+
+-->
+<plugin>
+ <extension-point id="operationApprovers" name="Operation Approvers" schema="schema/operationApprovers.exsd"/>
+ <extension-point id="historyListeners" name="Operation History Listeners" schema="schema/historyListeners.exsd"/>
+
+</plugin>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/pom.xml b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/pom.xml
new file mode 100644
index 00000000000..89242377479
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/pom.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>org.eclipse.papyrus.infra-emf</artifactId>
+ <groupId>org.eclipse.papyrus</groupId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>org.eclipse.papyrus.infra.emf.gmf</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/historyListeners.exsd b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/historyListeners.exsd
new file mode 100644
index 00000000000..89364178775
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/historyListeners.exsd
@@ -0,0 +1,91 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.papyrus.infra.gmfdiag.commands" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.papyrus.infra.gmfdiag.commands" id="historyListeners" name="historyListeners"/>
+ </appinfo>
+ <documentation>
+ Registration of history listeners to be attached to the operation history used by the Papyrus GMF diagrams (and transactional editing domains in general).
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="historyListener" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="historyListener">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ The Java class implementing of the history listener.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.core.commands.operations.IOperationHistoryListener"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 2.0
+ </documentation>
+ </annotation>
+
+
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2014 CEA and others.
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/operationApprovers.exsd b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/operationApprovers.exsd
new file mode 100644
index 00000000000..cfb6863fcb5
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/schema/operationApprovers.exsd
@@ -0,0 +1,78 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.papyrus.infra.emf.gmf" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.papyrus.infra.emf.gmf" id="operationApprovers" name="Operation Approviders"/>
+ </appinfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="operationApprover" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="operationApprover">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.core.commands.operations.IOperationApprover2"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="priority" type="string">
+ <annotation>
+ <documentation>
+ An integer value representing the priority of the operationApprover.
+High value means the operationApprover will be checked before those with lower value.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
+
+
+
+
+</schema>
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/CheckedOperationHistory.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/CheckedOperationHistory.java
new file mode 100644
index 00000000000..a9cc62ba62a
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/CheckedOperationHistory.java
@@ -0,0 +1,352 @@
+/*****************************************************************************
+ * Copyright (c) 2011, 2016 Atos, CEA, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mathieu Velten (Atos) - Initial API and implementation
+ * Christian W. Damus (CEA) - bugs 357250, 323802
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.ICompositeOperation;
+import org.eclipse.core.commands.operations.IOperationApprover;
+import org.eclipse.core.commands.operations.IOperationApprover2;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.OperationHistoryFactory;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.workspace.EMFCommandOperation;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.papyrus.infra.emf.internal.gmf.Activator;
+
+import com.google.common.collect.ObjectArrays;
+
+public class CheckedOperationHistory implements IOperationHistory {
+
+ private static class CheckedOperationHistoryHolder {
+
+ public static final CheckedOperationHistory instance = new CheckedOperationHistory();
+ }
+
+ public static CheckedOperationHistory getInstance() {
+ return CheckedOperationHistoryHolder.instance;
+ }
+
+ protected static final IOperationApprover2[] approversArray;
+
+ protected IOperationHistory history;
+
+ private static class ApproverPriorityPair implements Comparable<ApproverPriorityPair> {
+
+ public IOperationApprover2 approver;
+
+ public int priority;
+
+ @Override
+ public int compareTo(ApproverPriorityPair o) {
+ if (o.priority > priority) {
+ return 1;
+ } else if (o.priority < priority) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ }
+
+ static {
+ IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(Activator.PLUGIN_ID, "operationApprovers"); //$NON-NLS-1$
+ // Pre-2.0 extension point
+ IConfigurationElement[] legacyElements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.papyrus.infra.gmfdiag.commands", "operationApprover"); // Platform.getExtensionRegistry().getConfigurationElementsFor(Activator.PLUGIN_ID, //$NON-NLS-1$
+ // "operationApprover");
+ configElements = ObjectArrays.concat(configElements, legacyElements, IConfigurationElement.class);
+
+ List<ApproverPriorityPair> approverPriorityPairs = new LinkedList<ApproverPriorityPair>();
+ for (IConfigurationElement elem : configElements) {
+ if ("operationApprover".equals(elem.getName())) { //$NON-NLS-1$
+ try {
+ ApproverPriorityPair approverPriorityPair = new ApproverPriorityPair();
+ approverPriorityPair.approver = (IOperationApprover2) elem.createExecutableExtension("class"); //$NON-NLS-1$
+ approverPriorityPair.priority = Integer.parseInt(elem.getAttribute("priority")); //$NON-NLS-1$
+
+ approverPriorityPairs.add(approverPriorityPair);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in instantiation of operation approver.", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ Collections.sort(approverPriorityPairs);
+
+ approversArray = new IOperationApprover2[approverPriorityPairs.size()];
+
+ for (int i = 0; i < approversArray.length; i++) {
+ approversArray[i] = approverPriorityPairs.get(i).approver;
+ }
+ }
+
+ private CheckedOperationHistory() {
+ history = OperationHistoryFactory.getOperationHistory();
+
+ addRegisteredListeners(history);
+ }
+
+ /*
+ * Consult the IOperationApprovers to see if the proposed redo should be
+ * allowed.
+ */
+ protected IStatus getRedoApproval(IUndoableOperation operation, IAdaptable info) {
+ operation = unwrap(operation);
+ for (int i = 0; i < approversArray.length; i++) {
+ IStatus approval = approversArray[i].proceedRedoing(operation, this, info);
+ if (!approval.isOK()) {
+ return approval;
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * Consult the IOperationApprovers to see if the proposed undo should be
+ * allowed.
+ */
+ protected IStatus getUndoApproval(IUndoableOperation operation, IAdaptable info) {
+ operation = unwrap(operation);
+ for (int i = 0; i < approversArray.length; i++) {
+ IStatus approval = approversArray[i].proceedUndoing(operation, this, info);
+ if (!approval.isOK()) {
+ return approval;
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * Consult the IOperationApprovers to see if the proposed execution should
+ * be allowed.
+ *
+ * @since 3.2
+ */
+ protected IStatus getExecuteApproval(IUndoableOperation operation, IAdaptable info) {
+ operation = unwrap(operation);
+ for (int i = 0; i < approversArray.length; i++) {
+ IStatus approval = approversArray[i].proceedExecuting(operation, this, info);
+ if (!approval.isOK()) {
+ return approval;
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * the unified command stack wraps ICommand GMFtoEMFCommandWrapper
+ * which are wrapped in EMFCommandOperation,
+ * unwrap it before validation
+ *
+ * @param operation
+ * @return
+ */
+ protected IUndoableOperation unwrap(IUndoableOperation operation) {
+ if (operation instanceof EMFCommandOperation) {
+ Command emfCommand = ((EMFCommandOperation) operation).getCommand();
+ if (emfCommand instanceof GMFtoEMFCommandWrapper) {
+ ICommand gmfCommand = ((GMFtoEMFCommandWrapper) emfCommand).getGMFCommand();
+ if (gmfCommand != null) {
+ return gmfCommand;
+ }
+ }
+ }
+
+ return operation;
+ }
+
+ @Override
+ public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ // check with the operation approvers
+ IStatus status = getExecuteApproval(operation, info);
+ if (!status.isOK()) {
+ // not approved. No notifications are sent, just return the status.
+ return status;
+ }
+ return history.execute(operation, monitor, info);
+ }
+
+ @Override
+ public IStatus undo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ Assert.isNotNull(context);
+ IUndoableOperation operation = getUndoOperation(context);
+
+ // info if there is no operation
+ if (operation == null) {
+ return IOperationHistory.NOTHING_TO_UNDO_STATUS;
+ }
+
+ // check with the operation approvers
+ IStatus status = getUndoApproval(operation, info);
+ if (!status.isOK()) {
+ // not approved. No notifications are sent, just return the status.
+ return status;
+ }
+ return history.undo(context, monitor, info);
+ }
+
+ @Override
+ public IStatus redo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ Assert.isNotNull(context);
+ IUndoableOperation operation = getRedoOperation(context);
+
+ // info if there is no operation
+ if (operation == null) {
+ return IOperationHistory.NOTHING_TO_REDO_STATUS;
+ }
+
+ // check with the operation approvers
+ IStatus status = getRedoApproval(operation, info);
+ if (!status.isOK()) {
+ // not approved. No notifications are sent, just return the status.
+ return status;
+ }
+ return history.redo(context, monitor, info);
+ }
+
+ private static void addRegisteredListeners(IOperationHistory history) {
+ IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(Activator.PLUGIN_ID, "historyListeners"); //$NON-NLS-1$
+ // Pre-2.0 extension point
+ IConfigurationElement[] legacyElements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.papyrus.infra.gmfdiag.commands", "historyListeners"); //$NON-NLS-1$
+ configElements = ObjectArrays.concat(configElements, legacyElements, IConfigurationElement.class);
+
+ for (IConfigurationElement elem : configElements) {
+ if ("historyListener".equals(elem.getName())) { //$NON-NLS-1$
+ try {
+ IOperationHistoryListener listener = (IOperationHistoryListener) elem.createExecutableExtension("class"); //$NON-NLS-1$
+ history.addOperationHistoryListener(listener);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in instantiation of operation history listener.", e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ // all the following methods are pure delegation
+
+ @Override
+ public IStatus undoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ return history.undoOperation(operation, monitor, info);
+ }
+
+ @Override
+ public void setLimit(IUndoContext context, int limit) {
+ history.setLimit(context, limit);
+ }
+
+ @Override
+ public void replaceOperation(IUndoableOperation operation, IUndoableOperation[] replacements) {
+ history.replaceOperation(operation, replacements);
+ }
+
+ @Override
+ public void removeOperationHistoryListener(IOperationHistoryListener listener) {
+ history.removeOperationHistoryListener(listener);
+ }
+
+ @Override
+ public void removeOperationApprover(IOperationApprover approver) {
+ history.removeOperationApprover(approver);
+ }
+
+ @Override
+ public IStatus redoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+ return history.redoOperation(operation, monitor, info);
+ }
+
+ @Override
+ public void operationChanged(IUndoableOperation operation) {
+ history.operationChanged(operation);
+ }
+
+ @Override
+ public void openOperation(ICompositeOperation operation, int mode) {
+ history.openOperation(operation, mode);
+ }
+
+ @Override
+ public IUndoableOperation getUndoOperation(IUndoContext context) {
+ return history.getUndoOperation(context);
+ }
+
+ @Override
+ public IUndoableOperation[] getUndoHistory(IUndoContext context) {
+ return history.getUndoHistory(context);
+ }
+
+ @Override
+ public IUndoableOperation getRedoOperation(IUndoContext context) {
+ return history.getRedoOperation(context);
+ }
+
+ @Override
+ public IUndoableOperation[] getRedoHistory(IUndoContext context) {
+ return history.getRedoHistory(context);
+ }
+
+ @Override
+ public int getLimit(IUndoContext context) {
+ return history.getLimit(context);
+ }
+
+ @Override
+ public void dispose(IUndoContext context, boolean flushUndo, boolean flushRedo, boolean flushContext) {
+ history.dispose(context, flushUndo, flushRedo, flushContext);
+ }
+
+ @Override
+ public void closeOperation(boolean operationOK, boolean addToHistory, int mode) {
+ history.closeOperation(operationOK, addToHistory, mode);
+ }
+
+ @Override
+ public boolean canUndo(IUndoContext context) {
+ return history.canUndo(context);
+ }
+
+ @Override
+ public boolean canRedo(IUndoContext context) {
+ return history.canRedo(context);
+ }
+
+ @Override
+ public void addOperationHistoryListener(IOperationHistoryListener listener) {
+ history.addOperationHistoryListener(listener);
+ }
+
+ @Override
+ public void addOperationApprover(IOperationApprover approver) {
+ history.addOperationApprover(approver);
+ }
+
+ @Override
+ public void add(IUndoableOperation operation) {
+ history.add(operation);
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/EMFtoGMFCommandWrapper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/EMFtoGMFCommandWrapper.java
new file mode 100644
index 00000000000..ca30d10d1cf
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/EMFtoGMFCommandWrapper.java
@@ -0,0 +1,204 @@
+/***************************************************************************
+ * Copyright (c) 2007, 2016 Conselleria de Infraestructuras y Transporte, Generalitat de la Comunitat Valenciana, CEA, Christian W. Damus, and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: Mario Cervera Ubeda (Prodevelop)
+ * Christian W. Damus (CEA) - bug 430701
+ * Christian W. Damus - bug 485220
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
+import org.eclipse.gmf.runtime.common.core.command.AbstractCommand;
+import org.eclipse.gmf.runtime.common.core.command.CommandResult;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+
+/**
+ * A GMF Command that wraps an EMF command. Each method is redirected to the EMF one.
+ */
+public class EMFtoGMFCommandWrapper extends AbstractCommand implements ICommandWrapper<Command> {
+
+ private static Function<Command, ICommand> wrapperFunction = EMFtoGMFCommandWrapper::new;
+ private static Function<Command, ICommand> ndWrapperFunction = NonDirtying::new;
+
+ /**
+ * The wrapped EMF Command. Package-level visibility so that the command stack wrapper can
+ * access the field.
+ */
+ protected Command emfCommand;
+
+ /**
+ * This variable is used to avoid reentrant call in canUndo/undo/redo
+ *
+ * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=389382
+ */
+ protected boolean isBusy;
+
+ /**
+ * Constructor.
+ *
+ * @param emfCommand
+ * the emf command
+ */
+ public EMFtoGMFCommandWrapper(Command emfCommand) {
+ super(emfCommand.getLabel());
+ this.emfCommand = emfCommand;
+ }
+
+ /**
+ * Wraps the given {@code command}, accounting for possible non-dirty state.
+ *
+ * @param command
+ * a command to wrap
+ * @return the best wrapper for the {@code command}
+ */
+ public static ICommand wrap(Command command) {
+ if (command instanceof org.eclipse.emf.common.command.AbstractCommand.NonDirtying) {
+ return ndWrapperFunction.apply(command);
+ }
+ return wrapperFunction.apply(command);
+ }
+
+ /**
+ * Returns the wrapped EMF command.
+ *
+ * @return the EMF command
+ */
+ public Command getEMFCommand() {
+ return emfCommand;
+ }
+
+ @Override
+ public Command getWrappedCommand() {
+ return getEMFCommand();
+ }
+
+ @Override
+ protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
+
+ emfCommand.execute();
+
+ return CommandResult.newOKCommandResult();
+ }
+
+ @Override
+ protected CommandResult doRedoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
+
+ if (!isBusy) {
+ isBusy = true;
+ emfCommand.redo();
+ isBusy = false;
+ }
+
+ return CommandResult.newOKCommandResult();
+ }
+
+ @Override
+ protected CommandResult doUndoWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException {
+
+ if (!isBusy) {
+ isBusy = true;
+ emfCommand.undo();
+ isBusy = false;
+ }
+
+ return CommandResult.newOKCommandResult();
+ }
+
+ @Override
+ public boolean canExecute() {
+ return emfCommand.canExecute();
+ }
+
+ @Override
+ public void dispose() {
+ emfCommand.dispose();
+ }
+
+ @Override
+ public boolean canUndo() {
+ if (!isBusy) {
+ isBusy = true;
+ boolean res = emfCommand.canUndo();
+ isBusy = false;
+ return res;
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public List getAffectedFiles() {
+ ArrayList affectedFiles = new ArrayList();
+ Collection<?> affectedObjects = emfCommand.getAffectedObjects();
+ if (affectedObjects != null) {
+ for (Object o : affectedObjects) {
+ if (o instanceof EObject) {
+ o = ((EObject) o).eResource();
+ }
+ if (o instanceof Resource) {
+ o = WorkspaceSynchronizer.getFile((Resource) o);
+ }
+ if (o instanceof IFile) {
+ affectedFiles.add(o);
+ }
+ }
+ }
+ return affectedFiles;
+ }
+
+ @Override
+ public CommandResult getCommandResult() {
+ Collection<?> res = emfCommand.getResult();
+ if (res != null && !res.isEmpty()) {
+ if (res.size() == 1) {
+ return CommandResult.newOKCommandResult(res.iterator().next());
+ }
+ return CommandResult.newOKCommandResult(res);
+ }
+ return CommandResult.newOKCommandResult();
+ }
+
+ protected static void setWrapperFunction(Function<Command, ICommand> wrapperFunction) {
+ EMFtoGMFCommandWrapper.wrapperFunction = wrapperFunction;
+ }
+
+ protected static void setNonDirtyingWrapperFunction(Function<Command, ICommand> wrapperFunction) {
+ EMFtoGMFCommandWrapper.ndWrapperFunction = wrapperFunction;
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A non-dirtying wrapper for non-dirtying commands.
+ */
+ public static class NonDirtying extends EMFtoGMFCommandWrapper implements INonDirtying {
+
+ public NonDirtying(org.eclipse.emf.common.command.Command command) {
+ super(command);
+
+ if (!(command instanceof org.eclipse.emf.common.command.AbstractCommand.NonDirtying)) {
+ throw new IllegalArgumentException("Wrapped command is not non-dirtying"); //$NON-NLS-1$
+ }
+ }
+
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/GMFtoEMFCommandWrapper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/GMFtoEMFCommandWrapper.java
new file mode 100644
index 00000000000..3aa00f20abd
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/GMFtoEMFCommandWrapper.java
@@ -0,0 +1,183 @@
+/***************************************************************************
+ * Copyright (c) 2007, 2016 Conselleria de Infraestructuras y Transporte, Generalitat de la Comunitat Valenciana, CEA, Christian W. Damus, and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: Mario Cervera Ubeda (Prodevelop)
+ * Christian W. Damus (CEA) - bug 430701
+ * Christian W. Damus - bug 485220
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.function.Function;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+
+/**
+ * A EMF Command that wraps a GMF command. Each method is redirected to the GMF one.
+ */
+public class GMFtoEMFCommandWrapper extends AbstractCommand implements ICommandWrapper<ICommand> {
+
+ private static Function<ICommand, Command> wrapperFunction = GMFtoEMFCommandWrapper::new;
+ private static Function<ICommand, Command> ndWrapperFunction = NonDirtying::new;
+
+ /**
+ *
+ * Wraps the GMF command return value to be returned by this method.
+ *
+ * @return the possible return value from the GMF command
+ */
+ @Override
+ public Collection<?> getResult() {
+
+ Collection<Object> result = new ArrayList<Object>();
+ if (getGMFReturnValue() != null) {
+ result.add(getGMFReturnValue());
+ } // else return an empty collection
+
+ return result;
+ }
+
+ private Object getGMFReturnValue() {
+ if (getGMFCommand().getCommandResult() != null) {
+ return getGMFCommand().getCommandResult().getReturnValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * The wrapped GMF Command. Package-level visibility so that the command stack wrapper can
+ * access the field.
+ */
+ private final ICommand gmfCommand;
+
+ /**
+ * Constructor.
+ *
+ * @param gmfCommand
+ * the gmf command
+ */
+ public GMFtoEMFCommandWrapper(ICommand gmfCommand) {
+ super(gmfCommand.getLabel());
+ this.gmfCommand = gmfCommand;
+ }
+
+ /**
+ * Wraps the given {@code command}, accounting for possible non-dirty state.
+ *
+ * @param command
+ * a command to wrap
+ * @return the best wrapper for the {@code command}
+ */
+ public static Command wrap(ICommand command) {
+ if (command instanceof INonDirtying) {
+ return ndWrapperFunction.apply(command);
+ }
+ return wrapperFunction.apply(command);
+ }
+
+ /**
+ * Returns the wrapped GMF command.
+ *
+ * @return the GMF command
+ */
+ public ICommand getGMFCommand() {
+ return gmfCommand;
+ }
+
+ @Override
+ public ICommand getWrappedCommand() {
+ return getGMFCommand();
+ }
+
+ @Override
+ public boolean canExecute() {
+ return gmfCommand.canExecute();
+ }
+
+ @Override
+ public void dispose() {
+ gmfCommand.dispose();
+ }
+
+ @Override
+ public boolean canUndo() {
+ return gmfCommand.canUndo();
+ }
+
+ @Override
+ public void execute() {
+ try {
+ gmfCommand.execute(new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ }
+
+ }
+
+ @Override
+ public void redo() {
+ try {
+ gmfCommand.redo(new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ }
+
+ }
+
+ @Override
+ public void undo() {
+ try {
+ gmfCommand.undo(new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ }
+
+ }
+
+ @Override
+ public Collection<?> getAffectedObjects() {
+ return gmfCommand.getAffectedFiles();
+ }
+
+ @Override
+ public String getDescription() {
+ return gmfCommand.getLabel();
+ }
+
+ protected static void setWrapperFunction(Function<ICommand, Command> wrapperFunction) {
+ GMFtoEMFCommandWrapper.wrapperFunction = wrapperFunction;
+ }
+
+ protected static void setNonDirtyingWrapperFunction(Function<ICommand, Command> wrapperFunction) {
+ GMFtoEMFCommandWrapper.ndWrapperFunction = wrapperFunction;
+ }
+
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A non-dirtying wrapper for non-dirtying commands.
+ */
+ public static class NonDirtying extends GMFtoEMFCommandWrapper implements AbstractCommand.NonDirtying {
+
+ public NonDirtying(ICommand command) {
+ super(command);
+
+ if (!(command instanceof INonDirtying)) {
+ throw new IllegalArgumentException("Wrapped command is not non-dirtying"); //$NON-NLS-1$
+ }
+ }
+
+ }
+
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/ICommandWrapper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/ICommandWrapper.java
new file mode 100644
index 00000000000..74737ff7b6c
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/ICommandWrapper.java
@@ -0,0 +1,184 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+
+/**
+ * A protocol for wrappers that adapt commands of one framework to another.
+ *
+ * @param <T>
+ * the wrapped command type
+ */
+public interface ICommandWrapper<T> {
+ Registry REGISTRY = new Registry();
+
+ /**
+ * Unwraps the wrapper to get the wrapped command of the other framework.
+ *
+ * @return the wrapped command (never {@code null})
+ */
+ T getWrappedCommand();
+
+ /**
+ * Wraps a {@code command} as another {@code type}. This also handles wrappers
+ * that are not {@link ICommandWrapper}s, such as those provided by GMF or
+ * other projects externally to Papyrus.
+ *
+ * @param command
+ * a command to wrap
+ * @param type
+ * the command type to obtain
+ *
+ * @return the wrapped command
+ *
+ * @throws IllegalArgumentException
+ * if no wrapper is available of the required {@code type}
+ */
+ static <F, T> T wrap(F command, Class<T> type) {
+ return REGISTRY.getWrapper(command, type).apply(command);
+ }
+
+ /**
+ * Queries whether a {@code command} is a wrapper of some other type of command.
+ * This also handles wrappers that are not {@link ICommandWrapper}s, such as
+ * those provided by GMF or other projects externally to Papyrus.
+ *
+ * @param command
+ * a command to wrapper
+ * @param type
+ * the command type that perhaps it wraps
+ *
+ * @return the wrapped command
+ *
+ * @throws IllegalArgumentException
+ * if no wrapper is available of the required {@code type}
+ */
+ static <F, T> boolean isWrapper(T command, Class<F> ofType) {
+ return REGISTRY.hasUnwrapper(command, ofType);
+ }
+
+ /**
+ * Unwraps a {@code command} as another {@code type}. This also handles wrappers
+ * that are not {@link ICommandWrapper}s, such as those provided by GMF or
+ * other projects externally to Papyrus.
+ *
+ * @param command
+ * a command to unwrap
+ * @param type
+ * the command type to obtain
+ *
+ * @return the wrapped command
+ *
+ * @throws IllegalArgumentException
+ * if no wrapper is available of the required {@code type}
+ */
+ static <F, T> F unwrap(T command, Class<F> type) {
+ return REGISTRY.getUnwrapper(command, type).apply(command);
+ }
+
+ //
+ // Nested types
+ //
+
+ class Registry {
+ private final ConcurrentMap<Class<?>, ConcurrentMap<Class<?>, Function<?, ?>>> wrappers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<?>, ConcurrentMap<Class<?>, Function<?, ?>>> unwrappers = new ConcurrentHashMap<>();
+
+ private Registry() {
+ super();
+
+ registerWrapper(Command.class, ICommand.class, EMFtoGMFCommandWrapper::wrap);
+ registerUnwrapper(EMFtoGMFCommandWrapper.class, Command.class, EMFtoGMFCommandWrapper::getWrappedCommand);
+ registerWrapper(ICommand.class, Command.class, GMFtoEMFCommandWrapper::wrap);
+ registerUnwrapper(GMFtoEMFCommandWrapper.class, ICommand.class, GMFtoEMFCommandWrapper::getWrappedCommand);
+ }
+
+ /**
+ * Registers a functions to wrap commands of a source type as a target type.
+ *
+ * @param fromType
+ * the source command type
+ * @param toType
+ * the target command type
+ * @param wrapper
+ * the wrapper function
+ *
+ * @throws IllegalStateException
+ * if this pair of {@code fromType} and {@code toType} already has a wrapper registered
+ */
+ public <F, T> void registerWrapper(Class<F> fromType, Class<T> toType, Function<? super F, ? extends T> wrapper) {
+ ConcurrentMap<Class<?>, Function<?, ?>> wrappers = this.wrappers.computeIfAbsent(fromType, key -> new ConcurrentHashMap<>());
+ if (wrappers.putIfAbsent(toType, wrapper) != null) {
+ throw new IllegalStateException(String.format("Wrapper already registered for %s -> %s", fromType.getSimpleName(), toType.getSimpleName()));
+ }
+ }
+
+ /**
+ * Registers a function to unwrap commands of a source type to obtain the original command type.
+ *
+ * @param fromType
+ * the source command type
+ * @param toType
+ * the target command type
+ * @param unwrapper
+ * the unwrapper function
+ *
+ * @throws IllegalStateException
+ * if this pair of {@code fromType} and {@code toType} already has an unwrapper registered
+ */
+ public <F, T> void registerUnwrapper(Class<F> fromType, Class<T> toType, Function<? super F, ? extends T> unwrapper) {
+ ConcurrentMap<Class<?>, Function<?, ?>> unwrappers = this.unwrappers.computeIfAbsent(fromType, key -> new ConcurrentHashMap<>());
+ if (unwrappers.putIfAbsent(toType, unwrapper) != null) {
+ throw new IllegalStateException(String.format("Unwrapper already registered for %s <- %s", toType.getSimpleName(), fromType.getSimpleName()));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ <F, T> Function<F, T> getWrapper(F command, Class<T> type) {
+ return (Function<F, T>) wrappers.entrySet().stream()
+ .filter(e -> e.getKey().isInstance(command))
+ .flatMap(e -> e.getValue().entrySet().stream())
+ .filter(e -> type.isAssignableFrom(e.getKey()))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElseThrow(IllegalArgumentException::new);
+ }
+
+ <F, T> Function<T, F> getUnwrapper(T command, Class<F> type) {
+ return maybeGetUnwrapper(command, type).orElseThrow(IllegalArgumentException::new);
+ }
+
+ boolean hasUnwrapper(Object command, Class<?> type) {
+ return maybeGetUnwrapper(command, type).isPresent();
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ <F, T> Optional<Function<T, F>> maybeGetUnwrapper(T command, Class<F> type) {
+ return (Optional) unwrappers.entrySet().stream()
+ .filter(e -> e.getKey().isInstance(command))
+ .flatMap(e -> e.getValue().entrySet().stream())
+ .filter(e -> type.isAssignableFrom(e.getKey()))
+ .map(Map.Entry::getValue)
+ .findFirst();
+ }
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/INonDirtying.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/INonDirtying.java
new file mode 100644
index 00000000000..d00aec01d7d
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/INonDirtying.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.Command;
+
+
+/**
+ * A marker interface for GMF and GEF commands that are non-dirtying. For EMF {@link Command}s, use the {@link AbstractCommand.NonDirtying} interface.
+ *
+ * @see AbstractCommand.NonDirtying
+ */
+public interface INonDirtying {
+ // Just a marker interface
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NestingNotifyingWorkspaceCommandStack.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NestingNotifyingWorkspaceCommandStack.java
new file mode 100644
index 00000000000..e2d94ddabed
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NestingNotifyingWorkspaceCommandStack.java
@@ -0,0 +1,231 @@
+/*****************************************************************************
+ * Copyright (c) 2013, 2014 CEA LIST and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - adapted for self-nesting behaviour
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.transaction.RollbackException;
+
+
+public class NestingNotifyingWorkspaceCommandStack extends NotifyingWorkspaceCommandStack {
+
+ private NestingNotifyingWorkspaceCommandStack childCommandStack;
+
+ private final boolean nested;
+
+ private boolean executing;
+
+ protected IUndoContext defaultUndoContext;
+
+ public NestingNotifyingWorkspaceCommandStack(IOperationHistory history) {
+ this(false, history, null);
+ }
+
+ protected NestingNotifyingWorkspaceCommandStack(boolean nested, IOperationHistory history, IUndoContext defaultUndoContext) {
+ super(history);
+ this.nested = nested;
+ this.defaultUndoContext = defaultUndoContext;
+ }
+
+ protected NestingNotifyingWorkspaceCommandStack(boolean nested, IOperationHistory history) {
+ this(nested, history, computeNestedUndoContext());
+ }
+
+ private static IUndoContext computeNestedUndoContext() {
+ return new IUndoContext() {
+
+ public boolean matches(IUndoContext context) {
+ return context == this;
+ }
+
+ public String getLabel() {
+ return "Nested Undo Context";
+ }
+ };
+ }
+
+ @Override
+ public IUndoContext getDefaultUndoContext() {
+ if (defaultUndoContext == null) {
+ return super.getDefaultUndoContext();
+ }
+ return defaultUndoContext;
+ }
+
+ protected NestingNotifyingWorkspaceCommandStack getTopMostCommandStack() {
+ if (childCommandStack == null) {
+ return this;
+ }
+ return childCommandStack.getTopMostCommandStack();
+ }
+
+ protected void startNestedTransaction(Command command) {
+ if (childCommandStack != null) {
+ // Forwards to the current stack
+ childCommandStack.startNestedTransaction(command);
+ } else {
+ // Start a new nested transaction in a new nested Stack
+ childCommandStack = createNestedCommandStack(getOperationHistory());
+ childCommandStack.setEditingDomain(getDomain());
+
+ childCommandStack.execute(command);
+ }
+ }
+
+ protected NestingNotifyingWorkspaceCommandStack createNestedCommandStack(IOperationHistory history) {
+ return new NestingNotifyingWorkspaceCommandStack(true, history);
+ }
+
+ public void commit() {
+ if (childCommandStack != null) {
+ disposeLastCommandStack();
+ }
+ }
+
+ private boolean disposeLastCommandStack() {
+ if (childCommandStack == null) {
+ // I'm the last command stack
+ dispose();
+ return true;
+ }
+
+ // Propagates
+ if (childCommandStack.disposeLastCommandStack()) {
+ childCommandStack = null;
+ }
+
+ return false;
+ }
+
+ public void rollback() {
+ if (childCommandStack != null) {
+ while (canUndo()) {
+ undo();
+ }
+ disposeLastCommandStack();
+ }
+ }
+
+ @Override
+ public void execute(Command command) {
+ if (childCommandStack == null) {
+ if (!executing) {
+ executing = true;
+
+ try {
+ super.execute(command);
+ } finally {
+ executing = false;
+ }
+ } else {
+ // Re-entrant command execution goes on a nested stack
+ try {
+ startNestedTransaction(command);
+ commit();
+ } catch (OperationCanceledException e) {
+ rollback();
+ // Propagate
+ throw e;
+ }
+ }
+ } else {
+ childCommandStack.execute(command);
+ }
+ }
+
+ @Override
+ protected void handleError(Exception exception) {
+ if (nested && (exception instanceof RollbackException)) {
+ // A nested transaction rolled back
+ RollbackException rbe = (RollbackException) exception;
+ if (rbe.getStatus().getSeverity() == IStatus.CANCEL) {
+ // Propagate
+ throw new OperationCanceledException();
+ }
+ }
+
+ if (exception instanceof OperationCanceledException) {
+ rollback();
+ } else {
+ super.handleError(exception);
+ }
+ }
+
+ @Override
+ public Command getMostRecentCommand() {
+ if (childCommandStack == null) {
+ return super.getMostRecentCommand();
+ } else {
+ return childCommandStack.getMostRecentCommand();
+ }
+ }
+
+ @Override
+ public Command getRedoCommand() {
+ if (childCommandStack == null) {
+ return super.getRedoCommand();
+ } else {
+ return childCommandStack.getRedoCommand();
+ }
+ }
+
+ @Override
+ public Command getUndoCommand() {
+ if (childCommandStack == null) {
+ return super.getUndoCommand();
+ } else {
+ return childCommandStack.getUndoCommand();
+ }
+ }
+
+ @Override
+ public void undo() {
+ if (childCommandStack == null) {
+ super.undo();
+ } else {
+ childCommandStack.undo();
+ }
+ }
+
+ @Override
+ public boolean canUndo() {
+ if (childCommandStack == null) {
+ return super.canUndo();
+ } else {
+ return childCommandStack.canUndo();
+ }
+ }
+
+ @Override
+ public boolean canRedo() {
+ if (childCommandStack == null) {
+ return super.canRedo();
+ } else {
+ return childCommandStack.canRedo();
+ }
+ }
+
+ @Override
+ public void redo() {
+ if (childCommandStack == null) {
+ super.redo();
+ } else {
+ childCommandStack.redo();
+ }
+ }
+
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NotifyingWorkspaceCommandStack.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NotifyingWorkspaceCommandStack.java
new file mode 100644
index 00000000000..59dabfcca16
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/command/NotifyingWorkspaceCommandStack.java
@@ -0,0 +1,670 @@
+/*****************************************************************************
+ * Copyright (c) 2011, 2015 Atos, CEA, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mathieu Velten (Atos) - Initial API and implementation
+ * Arthur Daussy (Atos) - 363826: [Model Explorer] Drag and drop and undo, incorrect behavior
+ * Christian W. Damus (CEA) - 404220: Add contexts for tracking objects changed by operations (CDO)
+ * Christian W. Damus (CEA) - bug 402525
+ * Christian W. Damus (CEA) - bug 430648
+ * Christian W. Damus (CEA) - bug 431023
+ * Christian W. Damus (CEA) - bug 384169
+ * Christian W. Damus - bug 459746
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.emf.gmf.command;
+
+import static org.eclipse.papyrus.infra.emf.gmf.util.OperationUtils.anyDirtying;
+import static org.eclipse.papyrus.infra.emf.gmf.util.OperationUtils.isDirty;
+
+import java.util.Collection;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.commands.operations.UndoContext;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.CommandStackListener;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.transaction.NotificationFilter;
+import org.eclipse.emf.transaction.ResourceSetChangeEvent;
+import org.eclipse.emf.transaction.ResourceSetListenerImpl;
+import org.eclipse.emf.transaction.RollbackException;
+import org.eclipse.emf.transaction.Transaction;
+import org.eclipse.emf.transaction.impl.AbstractTransactionalCommandStack;
+import org.eclipse.emf.transaction.impl.EMFCommandTransaction;
+import org.eclipse.emf.transaction.impl.InternalTransaction;
+import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.TriggerCommandTransaction;
+import org.eclipse.emf.transaction.util.TriggerCommand;
+import org.eclipse.emf.workspace.EMFCommandOperation;
+import org.eclipse.emf.workspace.IResourceUndoContextPolicy;
+import org.eclipse.emf.workspace.IWorkspaceCommandStack;
+import org.eclipse.emf.workspace.ResourceUndoContext;
+import org.eclipse.emf.workspace.impl.EMFOperationTransaction;
+import org.eclipse.emf.workspace.impl.WorkspaceCommandStackImpl;
+import org.eclipse.emf.workspace.internal.EMFWorkspacePlugin;
+import org.eclipse.emf.workspace.internal.EMFWorkspaceStatusCodes;
+import org.eclipse.emf.workspace.internal.Tracing;
+import org.eclipse.emf.workspace.internal.l10n.Messages;
+import org.eclipse.gmf.runtime.emf.commands.core.command.EditingDomainUndoContext;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.papyrus.infra.emf.gmf.util.CommandUtils;
+
+/**
+ * Copied from WorkspaceCommandStackImpl but modify in order to change the
+ * IUndoContext. We want to make it point the the TransactionalEditingDomain. To
+ * see what really change in this class from original implementation look for
+ * "HAS CHANGE FROM ORIGINAL IMPLEMENTATION" in Java Doc.
+ *
+ */
+public class NotifyingWorkspaceCommandStack extends AbstractTransactionalCommandStack// AbstractTransactionalCommandStack
+ implements IWorkspaceCommandStack {
+
+ private final IOperationHistory history;
+
+ private DomainListener domainListener;
+
+ private IResourceUndoContextPolicy undoContextPolicy = IResourceUndoContextPolicy.DEFAULT;
+
+ private IUndoableOperation currentOperation;
+
+ private Set<Resource> historyAffectedResources;
+
+ /**
+ * HAS CHANGE FROM ORIGINAL IMPLEMENTATION TO USE {@link EditingDomainUndoContext}
+ */
+ private IUndoContext defaultContext = null;
+
+ private IUndoContext savedContext = null;
+
+ private IUndoableOperation mostRecentOperation;
+
+ /**
+ * Initializes me with the operation history to which I delegate command
+ * execution.
+ *
+ * @param history
+ * my operation history
+ */
+ public NotifyingWorkspaceCommandStack(IOperationHistory history) {
+ super();
+ this.history = history;
+ domainListener = new DomainListener();
+ defaultContext = new UndoContext() {
+
+ @Override
+ public String getLabel() {
+ return getDefaultUndoContextLabel();
+ }
+
+ @Override
+ public String toString() {
+ return getLabel();
+ }
+ };
+ }
+
+ /**
+ * map with registered listeners and the corresponding proxy registered to
+ * actual map
+ */
+ private Map<CommandStackListener, IOperationHistoryListener> proxyOperationListeners = new HashMap<CommandStackListener, IOperationHistoryListener>();
+
+ @Override
+ public void addCommandStackListener(final CommandStackListener listener) {
+ removeCommandStackListener(listener);
+ IOperationHistoryListener proxy = new IOperationHistoryListener() {
+
+ @Override
+ public void historyNotification(OperationHistoryEvent event) {
+ int type = event.getEventType();
+ // emf stack only needs to be notified when an operation is
+ // finished
+ if (OperationHistoryEvent.DONE == type || OperationHistoryEvent.REDONE == type || OperationHistoryEvent.UNDONE == type) {
+ listener.commandStackChanged(new EventObject(NotifyingWorkspaceCommandStack.this));
+ }
+ }
+ };
+ getOperationHistory().addOperationHistoryListener(proxy);
+ proxyOperationListeners.put(listener, proxy);
+ }
+
+ @Override
+ public void removeCommandStackListener(CommandStackListener listener) {
+ IOperationHistoryListener proxy = proxyOperationListeners.remove(listener);
+ if (proxy != null) {
+ getOperationHistory().removeOperationHistoryListener(proxy);
+ }
+ }
+
+ /**
+ * Extends the superclass implementation to add/remove listeners on the
+ * editing domain. HAS CHANGE FROM ORIGINAL IMPLEMENTATION TO USE {@link EditingDomainUndoContext}
+ */
+ @Override
+ public void setEditingDomain(InternalTransactionalEditingDomain domain) {
+ InternalTransactionalEditingDomain oldDomain = getDomain();
+ if (oldDomain != null) {
+ oldDomain.removeResourceSetListener(domainListener);
+ history.removeOperationHistoryListener(domainListener);
+ }
+ super.setEditingDomain(domain);
+ /*
+ * HAS CHANGE FROM ORIGINAL IMPLEMENTATION TO USE {@link
+ * EditingDomainUndoContext}
+ */
+ if (getDomain() != null) {
+ boolean domainHasChanged = oldDomain == null || !oldDomain.equals(getDomain());
+ if (domainHasChanged) {
+ defaultContext = new EditingDomainUndoContext(domain, getDefaultUndoContextLabel());
+ }
+ }
+ if (domain != null) {
+ history.addOperationHistoryListener(domainListener);
+ domain.addResourceSetListener(domainListener);
+ }
+ }
+
+ // Documentation copied from the method specification
+ @Override
+ public final IOperationHistory getOperationHistory() {
+ return history;
+ }
+
+ // Documentation copied from the method specification
+ @Override
+ public IUndoContext getDefaultUndoContext() {
+ return defaultContext;
+ }
+
+ /**
+ * Obtains the label to display for the default undo context that I apply to
+ * operations executed through me as {@link Command}s. Subclasses may
+ * override to customize the label.
+ *
+ * @return my default undo context label
+ *
+ * @since 1.2
+ */
+ protected String getDefaultUndoContextLabel() {
+ String domainID = (getDomain() == null) ? null : getDomain().getID();
+ if (domainID == null) {
+ domainID = String.valueOf(domainID); // guaranteed to be safe
+ }
+ return NLS.bind(Messages.cmdStkCtxLabel, domainID);
+ }
+
+ private final IUndoContext getSavedContext() {
+ if (savedContext == null) {
+ savedContext = new UndoContext() {
+
+ @Override
+ public String getLabel() {
+ return getSavepointUndoContextLabel();
+ }
+
+ @Override
+ public String toString() {
+ return getLabel();
+ }
+ };
+ }
+ return savedContext;
+ }
+
+ /**
+ * Obtains the label to display for the save-point undo context that I apply
+ * to the last operation in my {@linkplain #getDefaultUndoContext() default
+ * undo context} that was executed at the time save was performed (as
+ * indicated by invocation of the {@link #saveIsDone()} method). Subclasses
+ * may override to customize the label.
+ *
+ * @return my save-point undo context label
+ *
+ * @since 1.2
+ */
+ protected String getSavepointUndoContextLabel() {
+ String domainID = (getDomain() == null) ? null : getDomain().getID();
+ if (domainID == null) {
+ domainID = String.valueOf(domainID); // guaranteed to be safe
+ }
+ return NLS.bind(Messages.cmdStkSaveCtxLabel, domainID);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 1.1
+ */
+ @Override
+ protected void doExecute(Command command, Map<?, ?> options) throws InterruptedException, RollbackException {
+ IUndoableOperation oper = CommandUtils.wrap(getDomain(), command, options);
+ // add the appropriate context
+ oper.addContext(getDefaultUndoContext());
+ try {
+ IStatus status = history.execute(oper, new NullProgressMonitor(), null);
+ if (status.getSeverity() >= IStatus.ERROR) {
+ // the transaction must have rolled back if the status was
+ // error or worse
+ RollbackException exc = new RollbackException(status);
+ Tracing.throwing(WorkspaceCommandStackImpl.class, "execute", exc); //$NON-NLS-1$
+ throw exc;
+ }
+ notifyListeners();
+ } catch (ExecutionException e) {
+ Tracing.catching(WorkspaceCommandStackImpl.class, "execute", e); //$NON-NLS-1$
+ command.dispose();
+ if (e.getCause() instanceof RollbackException) {
+ // throw the rollback
+ RollbackException exc = (RollbackException) e.getCause();
+ Tracing.throwing(WorkspaceCommandStackImpl.class, "execute", exc); //$NON-NLS-1$
+ throw exc;
+ } else if (e.getCause() instanceof RuntimeException) {
+ // throw the programming error
+ RuntimeException exc = (RuntimeException) e.getCause();
+ Tracing.throwing(WorkspaceCommandStackImpl.class, "execute", exc); //$NON-NLS-1$
+ throw exc;
+ } else {
+ // log the problem. We can't rethrow whatever it was
+ handleError(e);
+ }
+ }
+ }
+
+ /**
+ * Queries whether we can undo my default undo context in my operation
+ * history.
+ */
+ @Override
+ public boolean canUndo() {
+ return getOperationHistory().canUndo(getDefaultUndoContext());
+ }
+
+ /**
+ * Undoes my default undo context in my operation history.
+ */
+ @Override
+ public void undo() {
+ try {
+ getOperationHistory().undo(getDefaultUndoContext(), new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ Tracing.catching(WorkspaceCommandStackImpl.class, "undo", e); //$NON-NLS-1$
+ // can't throw anything from this method
+ handleError(e);
+ } finally {
+ // notify even if there was an error; clients should check to see
+ // that the command stack is flushed
+ notifyListeners();
+ }
+ }
+
+ /**
+ * Queries whether we can redo my default undo context in my operation
+ * history.
+ */
+ @Override
+ public boolean canRedo() {
+ return getOperationHistory().canRedo(getDefaultUndoContext());
+ }
+
+ /**
+ * Redoes my default undo context in my operation history.
+ */
+ @Override
+ public void redo() {
+ try {
+ getOperationHistory().redo(getDefaultUndoContext(), new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ Tracing.catching(WorkspaceCommandStackImpl.class, "redo", e); //$NON-NLS-1$
+ // can't throw anything from this method
+ handleError(e);
+ } finally {
+ // notify even if there was an error; clients should check to see
+ // that the command stack is flushed
+ notifyListeners();
+ }
+ }
+
+ /**
+ * Disposes my default undo context in my operation history.
+ */
+ @Override
+ public void flush() {
+ getOperationHistory().dispose(getDefaultUndoContext(), true, true, true);
+ if (savedContext != null) {
+ getOperationHistory().dispose(getSavedContext(), true, true, true);
+ savedContext = null;
+ }
+ }
+
+ /**
+ * Gets the command from the most recently executed, done, or redone
+ * operation.
+ */
+ @Override
+ public Command getMostRecentCommand() {
+ Command result = null;
+ if (mostRecentOperation instanceof EMFCommandOperation) {
+ result = ((EMFCommandOperation) mostRecentOperation).getCommand();
+ }
+ return result;
+ }
+
+ /**
+ * Gets the command from the top of the undo history, if any.
+ */
+ @Override
+ public Command getUndoCommand() {
+ Command result = null;
+ IUndoableOperation topOperation = getOperationHistory().getUndoOperation(getDefaultUndoContext());
+ if (topOperation instanceof EMFCommandOperation) {
+ result = ((EMFCommandOperation) topOperation).getCommand();
+ }
+ return result;
+ }
+
+ /**
+ * Gets the command from the top of the redo history, if any.
+ */
+ @Override
+ public Command getRedoCommand() {
+ Command result = null;
+ IUndoableOperation topOperation = getOperationHistory().getRedoOperation(getDefaultUndoContext());
+ if (topOperation instanceof EMFCommandOperation) {
+ result = ((EMFCommandOperation) topOperation).getCommand();
+ }
+ return result;
+ }
+
+ // Documentation copied from the method specification
+ @Override
+ public EMFCommandTransaction createTransaction(Command command, Map<?, ?> options) throws InterruptedException {
+ EMFCommandTransaction result;
+ if (command instanceof TriggerCommand) {
+ result = new TriggerCommandTransaction((TriggerCommand) command, getDomain(), options);
+ } else {
+ result = new EMFOperationTransaction(command, getDomain(), options);
+ }
+ result.start();
+ return result;
+ }
+
+ // Documentation copied from the method specification
+ @Override
+ public void executeTriggers(Command command, List<Command> triggers, Map<?, ?> options) throws InterruptedException, RollbackException {
+ if (!triggers.isEmpty()) {
+ TriggerCommand trigger = (command == null) ? new TriggerCommand(triggers) : new TriggerCommand(command, triggers);
+ InternalTransaction tx = createTransaction(trigger, makeTriggerTransactionOptions(options));
+ try {
+ trigger.execute();
+ InternalTransaction parent = (InternalTransaction) tx.getParent();
+ // shouldn't be null if we're executing triggers!
+ if (parent != null) {
+ parent.addTriggers(trigger);
+ }
+ // commit the transaction now
+ tx.commit();
+ } catch (RuntimeException e) {
+ Tracing.catching(WorkspaceCommandStackImpl.class, "executeTriggers", e); //$NON-NLS-1$
+ IStatus status;
+ if (e instanceof OperationCanceledException) {
+ status = Status.CANCEL_STATUS;
+ } else {
+ status = new Status(IStatus.ERROR, EMFWorkspacePlugin.getPluginId(), EMFWorkspaceStatusCodes.PRECOMMIT_FAILED, Messages.precommitFailed, e);
+ }
+ RollbackException rbe = new RollbackException(status);
+ Tracing.throwing(WorkspaceCommandStackImpl.class, "executeTriggers", rbe); //$NON-NLS-1$
+ throw rbe;
+ } finally {
+ if ((tx != null) && (tx.isActive())) {
+ // roll back because an uncaught exception occurred
+ rollback(tx);
+ }
+ }
+ }
+ }
+
+ // Documentation copied from the method specification
+ @Override
+ public void dispose() {
+ setEditingDomain(null); // remove listeners
+ domainListener = null;
+ historyAffectedResources = null;
+ mostRecentOperation = null;
+
+ // remove listeners registered in opertationHistory
+ Collection<IOperationHistoryListener> values = proxyOperationListeners.values();
+ for (IOperationHistoryListener proxy : values) {
+ getOperationHistory().removeOperationHistoryListener(proxy);
+ }
+ proxyOperationListeners.clear();
+
+ // Flush default and savepoint undo contexts
+ flush();
+ }
+
+ /**
+ * Obtains my resource undo-context policy.
+ *
+ * @return my resource undo-context policy
+ *
+ * @since 1.3
+ */
+ public IResourceUndoContextPolicy getResourceUndoContextPolicy() {
+ return undoContextPolicy;
+ }
+
+ /**
+ * Sets my resource undo-context policy.
+ *
+ * @param policy
+ * my new policy, or <code>null</code> to restore the default
+ *
+ * @since 1.3
+ */
+ public void setResourceUndoContextPolicy(IResourceUndoContextPolicy policy) {
+ this.undoContextPolicy = policy;
+ }
+
+ /**
+ * A listener on the editing domain and operation history that tracks which
+ * resources are changed by an operation and attaches the appropriate {@link ResourceUndoContext} to it when it completes.
+ *
+ * @author Christian W. Damus (cdamus)
+ */
+ private class DomainListener extends ResourceSetListenerImpl implements IOperationHistoryListener {
+
+ @Override
+ public void historyNotification(OperationHistoryEvent event) {
+ final IUndoableOperation operation = event.getOperation();
+ switch (event.getEventType()) {
+ case OperationHistoryEvent.ABOUT_TO_EXECUTE:
+ // set up to remember affected resources in case we make EMF
+ // changes
+ currentOperation = operation;
+ historyAffectedResources = new java.util.HashSet<Resource>();
+ break;
+ case OperationHistoryEvent.DONE:
+ if ((historyAffectedResources != null) && !historyAffectedResources.isEmpty()) {
+ // add my undo context to the operation that has
+ // completed, but only if the operation actually changed
+ // any of my resources (in case this history is shared
+ // with other domains)
+ for (Resource next : historyAffectedResources) {
+ operation.addContext(new ResourceUndoContext(getDomain(), next));
+ }
+ }
+ currentOperation = null;
+ historyAffectedResources = null;
+ if (operation.hasContext(getDefaultUndoContext())) {
+ mostRecentOperation = operation;
+ }
+ break;
+ case OperationHistoryEvent.OPERATION_NOT_OK:
+ // just forget about the context because this operation
+ // failed
+ currentOperation = null;
+ historyAffectedResources = null;
+ break;
+ case OperationHistoryEvent.UNDONE:
+ case OperationHistoryEvent.REDONE:
+ if (operation.hasContext(getDefaultUndoContext())) {
+ mostRecentOperation = operation;
+ }
+ break;
+ case OperationHistoryEvent.OPERATION_REMOVED:
+ if (operation == mostRecentOperation) {
+ mostRecentOperation = null;
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void resourceSetChanged(ResourceSetChangeEvent event) {
+ IUndoableOperation operation = null;
+ Set<Resource> unloaded = getUnloadedResources(event.getNotifications());
+ if (unloaded != null) {
+ // dispose their undo contexts
+ for (Resource next : unloaded) {
+ getOperationHistory().dispose(new ResourceUndoContext(getDomain(), next), true, true, true);
+ }
+ }
+ Transaction tx = event.getTransaction();
+ if (tx != null) {
+ operation = (IUndoableOperation) tx.getOptions().get(EMFWorkspacePlugin.OPTION_OWNING_OPERATION);
+ }
+ if (operation == null) {
+ operation = currentOperation;
+ }
+ if (operation != null) {
+ Set<Resource> affectedResources = getResourceUndoContextPolicy().getContextResources(operation, event.getNotifications());
+ if (unloaded != null) {
+ // don't add these resources to the operation
+ affectedResources.removeAll(unloaded);
+ }
+ if (!affectedResources.isEmpty()) {
+ // add any resource undo contexts to this operation that are
+ // not already applied
+ for (Resource next : affectedResources) {
+ ResourceUndoContext ctx = new ResourceUndoContext(getDomain(), next);
+ if (!operation.hasContext(ctx)) {
+ operation.addContext(ctx);
+ }
+ }
+ }
+ if (historyAffectedResources != null) {
+ // there is an operation executing on our history that is
+ // affecting my editing domain. Remember the affected
+ // resources.
+ historyAffectedResources.addAll(affectedResources);
+ }
+
+ hookUndoContexts(operation, event);
+ }
+ }
+
+ /**
+ * Finds resources that have sent unload notifications.
+ *
+ * @param notifications
+ * notifications received from a transaction
+ * @return a set of resources that the notifications indicate have been
+ * unloaded, or <code>null</code> if none
+ */
+ private Set<Resource> getUnloadedResources(Collection<Notification> notifications) {
+ Set<Resource> result = null;
+ for (Notification next : notifications) {
+ if (NotificationFilter.RESOURCE_UNLOADED.matches(next)) {
+ if (result == null) {
+ result = new java.util.HashSet<Resource>();
+ }
+ result.add((Resource) next.getNotifier());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isPostcommitOnly() {
+ // only interested in post-commit "resourceSetChanged" event
+ return true;
+ }
+ }
+
+ /**
+ * A hook for subclasses to attach additional undo-contexts to an {@code operation} based on changes
+ * in the resource set.
+ *
+ * @param operation
+ * an operation that has been executed (never {@code null})
+ * @param event
+ * the description of changes made by the {@code operation} in the resource set
+ */
+ protected void hookUndoContexts(IUndoableOperation operation, ResourceSetChangeEvent event) {
+ // pass
+ }
+
+
+ @Override
+ public boolean isSaveNeeded() {
+ // This class inherits from AbstractTransactionalCommandStack which in turn inherits from BasicCommandStack.
+
+ // The operation isSaveNeeded is defined in BasicCommandStack. In order to work, it requires an update of the
+ // variables "saveIndex" and "top" which is done in BasicCommandStack::execute. However, this operation is overridden
+ // in method AbstractTransactionalCommandStack::execute which never calls the superclass method BasicCommandStack::execute.
+ // Thus, we cannot rely on the super class method of isSaveNeeded (although it seems to work in some cases).
+ // => so we have to implement the isSaveNeeded method here.
+ IUndoableOperation nextUndoableOperation = history.getUndoOperation(getDefaultUndoContext());
+ if (nextUndoableOperation == null) {
+ // this is the last undoable operation. But the document might have been saved at some operation now on the redo stack
+ return savedContext != null;
+ }
+ return savedContext != null ? !nextUndoableOperation.hasContext(getSavedContext()) && isDirty(history.getUndoHistory(getDefaultUndoContext()), history.getRedoHistory(getDefaultUndoContext()), history.getUndoOperation(savedContext))
+ : anyDirtying(history.getUndoHistory(getDefaultUndoContext()));
+ }
+
+ @Override
+ public void saveIsDone() {
+ // See comment for isSaveNeeded
+ if (savedContext != null) {
+ // The save context is only stored on one operation. We must
+ // remove it from any other operation that may have contained it
+ // before.
+ IUndoableOperation[] undoableOperations = history.getUndoHistory(getSavedContext());
+ for (int i = 0; i < undoableOperations.length; i++) {
+ undoableOperations[i].removeContext(getSavedContext());
+ }
+ IUndoableOperation[] redoableOperations = history.getRedoHistory(getSavedContext());
+ for (int i = 0; i < redoableOperations.length; i++) {
+ redoableOperations[i].removeContext(getSavedContext());
+ }
+ }
+ IUndoableOperation nextUndoableOperation = history.getUndoOperation(getDefaultUndoContext());
+ if (nextUndoableOperation == null) {
+ // We no longer have any operation that was saved
+ savedContext = null;
+ return;
+ }
+ nextUndoableOperation.addContext(getSavedContext());
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandTreeIterator.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandTreeIterator.java
new file mode 100644
index 00000000000..6f58619012f
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandTreeIterator.java
@@ -0,0 +1,175 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.emf.gmf.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.papyrus.infra.emf.gmf.command.ICommandWrapper;
+
+/**
+ * An iterator over the tree structure of EMF, GEF, and GMF commands that returns leaf commands of one or all of these kinds,
+ * with accounting for the various kinds of wrappers employed to intermix them. This iterator does not support the
+ * optional {@link Iterator#remove()} operation.
+ */
+public class CommandTreeIterator<C> implements Iterator<C> {
+ private final Class<C> type;
+
+ private Iterator<?> current;
+ private List<Iterator<?>> iterators = new ArrayList<Iterator<?>>();
+
+ private C preparedNext;
+
+ private CommandTreeIterator(Object root, Class<C> type) {
+ super();
+
+ this.type = type;
+
+ root = unwrap(root);
+
+ if (isCompound(root)) {
+ pushIterator(root);
+ } else {
+ prepareNext(root);
+ }
+ }
+
+ public static CommandTreeIterator<Command> iterateEMF(Object command) {
+ return iterate(command, Command.class);
+ }
+
+ public static CommandTreeIterator<ICommand> iterateGMF(Object command) {
+ return iterate(command, ICommand.class);
+ }
+
+ public static CommandTreeIterator<?> iterate(Object command) {
+ return iterate(command, Object.class);
+ }
+
+ public static <C> CommandTreeIterator<C> iterate(Object command, Class<C> leafCommandType) {
+ return new CommandTreeIterator<C>(command, leafCommandType);
+ }
+
+ private boolean prepareNext(Object command) {
+ if (type.isInstance(command)) {
+ preparedNext = type.cast(command);
+ }
+
+ return preparedNext != null;
+ }
+
+ private Iterator<?> pushIterator(Object compoundCommand) {
+ if (current != null) {
+ iterators.add(current);
+ }
+ current = iterator(compoundCommand);
+ return current;
+ }
+
+ private Iterator<?> popIterator() {
+ if (iterators.isEmpty()) {
+ current = null;
+ } else {
+ current = iterators.remove(iterators.size() - 1);
+ }
+
+ return current;
+ }
+
+ private Object internalNext() {
+ Object result = null;
+
+ while ((result == null) && (current != null)) {
+ if (current.hasNext()) {
+ Object next = unwrap(current.next());
+ if (isCompound(next)) {
+ // Dive into it
+ pushIterator(next);
+ } else {
+ // We have the next leaf
+ result = next;
+ }
+ } else {
+ popIterator();
+ }
+ }
+
+ return result;
+ }
+
+ boolean isDone() {
+ return (current == null) && iterators.isEmpty();
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (!isDone() && (preparedNext == null)) {
+ Object next = internalNext();
+ if (type.isInstance(next)) {
+ preparedNext = type.cast(next);
+ }
+ }
+
+ return preparedNext != null;
+ }
+
+ @Override
+ public C next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ C result = preparedNext;
+ preparedNext = null;
+ return result;
+ }
+
+ /**
+ * Remove is not supported.
+ */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("remove"); //$NON-NLS-1$
+ }
+
+ private Object unwrap(Object command) {
+ Object result = command;
+
+ if (command instanceof ICommandWrapper<?>) {
+ return ((ICommandWrapper<?>) command).getWrappedCommand();
+ } else if (ICommandWrapper.isWrapper(command, Object.class)) {
+ // Try a registered foreign wrapper
+ return ICommandWrapper.unwrap(command, Object.class);
+ }
+
+ if (result != command) {
+ // Could be turtles all the way down
+ result = unwrap(result);
+ }
+
+ return result;
+ }
+
+ private boolean isCompound(Object command) {
+ return CommandUtils.isCompound(command);
+ }
+
+ private Iterator<?> iterator(Object compoundCommand) {
+ return CommandUtils.getChildren(compoundCommand).iterator();
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandUtils.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandUtils.java
new file mode 100644
index 00000000000..5048c89d699
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/CommandUtils.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ */
+package org.eclipse.papyrus.infra.emf.gmf.util;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.CompoundCommand;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.workspace.EMFCommandOperation;
+import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.gmf.runtime.common.core.command.ICompositeCommand;
+import org.eclipse.papyrus.infra.emf.gmf.command.INonDirtying;
+
+
+/**
+ * Utilities for working with potentially non-dirtying EMF, GEF, and GMF commands.
+ *
+ * @see INonDirtying
+ * @see AbstractCommand.NonDirtying
+ */
+public class CommandUtils {
+
+ public static final Registry REGISTRY = new Registry();
+
+ /**
+ * Not instantiable by clients.
+ */
+ private CommandUtils() {
+ super();
+ }
+
+ public static String getLabel(Object command) {
+ return REGISTRY.getLabeller(command).apply(command);
+ }
+
+ public static boolean isCompound(Object command) {
+ return REGISTRY.hasDecomposer(command);
+ }
+
+ public static <T, C extends T> Iterable<T> getChildren(C compoundCommand) {
+ return REGISTRY.<T, C> getDecomposer(compoundCommand).apply(compoundCommand);
+ }
+
+ public static <T> T chain(T command1, T command2) {
+ return REGISTRY.getComposer(command1).apply(command1, command2);
+ }
+
+ public static boolean isNonDirtying(Object command) {
+ return (command instanceof INonDirtying)
+ || (command instanceof AbstractCommand.NonDirtying);
+ }
+
+ public static IUndoableOperation wrap(TransactionalEditingDomain domain, Command command) {
+ if (command instanceof AbstractCommand.NonDirtying) {
+ return new NonDirtyingEMFCommandOperation(domain, command);
+ }
+ return new EMFCommandOperation(domain, command);
+ }
+
+ public static IUndoableOperation wrap(TransactionalEditingDomain domain, Command command, Map<?, ?> options) {
+ if (command instanceof AbstractCommand.NonDirtying) {
+ return new NonDirtyingEMFCommandOperation(domain, command, options);
+ }
+ return new EMFCommandOperation(domain, command, options);
+ }
+
+ public static CompoundCommand nonDirtyingEMFCompound() {
+ return new NonDirtyingEMFCompoundCommand();
+ }
+
+ public static Command chain(Command command1, Command command2) {
+ if ((command1 instanceof AbstractCommand.NonDirtying) && (command2 instanceof AbstractCommand.NonDirtying)) {
+ return new NonDirtyingEMFCompoundCommand().chain(command1).chain(command2);
+ }
+ return command1.chain(command2);
+ }
+
+ public static CompositeCommand nonDirtyingGMFComposite(String label) {
+ return new NonDirtyingGMFCompositeCommand(label);
+ }
+
+ public static ICommand compose(ICommand command1, ICommand command2) {
+ if ((command1 instanceof INonDirtying) && (command2 instanceof INonDirtying)) {
+ return new NonDirtyingGMFCompositeCommand(command1.getLabel()).compose(command1).compose(command2);
+ }
+ return command1.compose(command2);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class NonDirtyingEMFCommandOperation extends EMFCommandOperation implements INonDirtying {
+
+ NonDirtyingEMFCommandOperation(TransactionalEditingDomain domain, Command command, Map<?, ?> options) {
+ super(domain, checkCommand(command), options);
+ }
+
+ NonDirtyingEMFCommandOperation(TransactionalEditingDomain domain, Command command) {
+ super(domain, checkCommand(command));
+ }
+
+ static Command checkCommand(Command command) {
+ if (!(command instanceof AbstractCommand.NonDirtying)) {
+ throw new IllegalStateException("Attempt to wrap dirtying command in a non-dirtying operation."); //$NON-NLS-1$
+ }
+ return command;
+ }
+ }
+
+ private static class NonDirtyingEMFCompoundCommand extends CompoundCommand implements AbstractCommand.NonDirtying {
+
+ @Override
+ public void append(Command command) {
+ checkNonDirtying(command);
+ super.append(command);
+ }
+
+ @Override
+ public boolean appendAndExecute(Command command) {
+ checkNonDirtying(command);
+ return super.appendAndExecute(command);
+ }
+
+ @Override
+ public boolean appendIfCanExecute(Command command) {
+ checkNonDirtying(command);
+ return super.appendIfCanExecute(command);
+ }
+
+ @Override
+ public Command chain(Command command) {
+ append(command);
+ return this;
+ }
+
+ private void checkNonDirtying(Command command) {
+ if (!(command instanceof AbstractCommand.NonDirtying)) {
+ throw new IllegalArgumentException("Attempt to append a dirtying command to a non-dirtying compound."); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private static class NonDirtyingGMFCompositeCommand extends CompositeCommand implements INonDirtying {
+
+ NonDirtyingGMFCompositeCommand(String label) {
+ super(label);
+ }
+
+ @Override
+ public void add(IUndoableOperation operation) {
+ checkNonDirtying(operation);
+ super.add(operation);
+ }
+
+ private void checkNonDirtying(IUndoableOperation operation) {
+ if (!(operation instanceof INonDirtying)) {
+ throw new IllegalArgumentException("Attempt to append a dirtying operation to a non-dirtying composite."); //$NON-NLS-1$
+ }
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ public static class Registry {
+ private final ConcurrentMap<Class<?>, BiFunction<?, ?, ?>> composers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<?>, Function<?, ? extends Iterable<?>>> decomposers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<?>, Function<?, String>> labellers = new ConcurrentHashMap<>();
+
+ @SuppressWarnings("unchecked")
+ private Registry() {
+ super();
+
+ registerComposer(Command.class, CommandUtils::chain);
+ registerDecomposer(CompoundCommand.class, CompoundCommand::getCommandList);
+ registerLabeller(Command.class, Command::getLabel);
+
+ registerComposer(ICommand.class, CommandUtils::compose);
+ this.<ICommand, ICompositeCommand> registerDecomposer(ICompositeCommand.class, c -> () -> c.iterator());
+ registerLabeller(IUndoableOperation.class, IUndoableOperation::getLabel);
+ }
+
+ /**
+ * Registers a function to compose two commands together into a compound of some sort.
+ *
+ * @param commandType
+ * the composable command type
+ * @param composer
+ * the composer function
+ *
+ * @throws IllegalStateException
+ * if this {@code commandType} already has a composer registered
+ */
+ public <T, C extends T> void registerComposer(Class<T> commandType, BiFunction<? super T, ? super T, ? extends C> composer) {
+ if (composers.putIfAbsent(commandType, composer) != null) {
+ throw new IllegalStateException(String.format("Composer already registered for %s", commandType.getSimpleName()));
+ }
+ }
+
+ /**
+ * Registers a function to decompose compounds commands of some type to obtain the composed commands.
+ *
+ * @param compoundType
+ * the compound command type
+ * @param decomposer
+ * the decomposer function
+ *
+ * @throws IllegalStateException
+ * if this {@code compoundType} already has ae decomposer registered
+ */
+ public <T, C extends T> void registerDecomposer(Class<C> compoundType, Function<? super C, ? extends Iterable<? extends T>> decomposer) {
+ if (decomposers.putIfAbsent(compoundType, decomposer) != null) {
+ throw new IllegalStateException(String.format("Decomposer already registered for %s", compoundType.getSimpleName()));
+ }
+ }
+
+ /**
+ * Registers a function to get the label of a command.
+ *
+ * @param commandType
+ * the labelled command type
+ * @param labeller
+ * the labeller function
+ *
+ * @throws IllegalStateException
+ * if this {@code commandType} already has a labeller registered
+ */
+ public <T> void registerLabeller(Class<T> commandType, Function<? super T, String> labeller) {
+ if (labellers.putIfAbsent(commandType, labeller) != null) {
+ throw new IllegalStateException(String.format("Labeller already registered for %s", commandType.getSimpleName()));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ <T, C extends T> BiFunction<T, T, C> getComposer(T command) {
+ return (BiFunction<T, T, C>) composers.entrySet().stream()
+ .filter(e -> e.getKey().isInstance(command))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElseThrow(IllegalArgumentException::new);
+ }
+
+ <T, C extends T> Function<? super C, ? extends Iterable<T>> getDecomposer(T command) {
+ return maybeGetDecomposer(command).orElseThrow(IllegalArgumentException::new);
+ }
+
+ boolean hasDecomposer(Object command) {
+ return maybeGetDecomposer(command).isPresent();
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ <T, C extends T> Optional<Function<? super C, ? extends Iterable<T>>> maybeGetDecomposer(C compound) {
+ return (Optional) decomposers.entrySet().stream()
+ .filter(e -> e.getKey().isInstance(compound))
+ .map(Map.Entry::getValue)
+ .findFirst();
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> Function<T, String> getLabeller(T command) {
+ return (Function<T, String>) labellers.entrySet().stream()
+ .filter(e -> e.getKey().isInstance(command))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElse(Object::toString);
+ }
+ }
+
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/GMFUnsafe.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/GMFUnsafe.java
new file mode 100644
index 00000000000..dccad9b8d38
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/GMFUnsafe.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.emf.gmf.util;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.transaction.RollbackException;
+import org.eclipse.emf.transaction.Transaction;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.papyrus.infra.emf.internal.gmf.Activator;
+
+
+/**
+ * Utilities for operations in the GMF context that we might consider as "unsafe" or exceptional cases.
+ */
+public class GMFUnsafe {
+
+ /**
+ * Not instantiable by clients.
+ */
+ private GMFUnsafe() {
+ super();
+ }
+
+ /**
+ * Performs an unsafe write to the model. The editing domain may or may not already have an active transaction, which may or may not be read-only;
+ * it does not matter. In any case, the changes performed will not be recorded for undo/redo or roll-back. Thus, this is appropriate only for use
+ * cases such as synchronization of canonical views, which are not considered logically as abstract model edits (though they be concrete changes).
+ *
+ * @param domain
+ * an editing domain that may or may not have a transaction in progress
+ * @param writeOperation
+ * an operation that will make unchecked/unsafe changes to the editing {@code domain}
+ *
+ * @throws RollbackException
+ * if the unprotected write transaction fails to commit. Note that this could occlude an uncaught exception thrown by the {@code writeOperation} runnable
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting for the unprotected write transaction to start
+ */
+ public static void write(TransactionalEditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
+ runUnprotected(domain, writeOperation);
+ }
+
+ private static void runUnprotected(TransactionalEditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
+ InternalTransactionalEditingDomain internalDomain = (InternalTransactionalEditingDomain) domain;
+ Transaction unprotected = internalDomain.startTransaction(false, Collections.singletonMap(Transaction.OPTION_UNPROTECTED, true));
+ try {
+ writeOperation.run();
+ } finally {
+ unprotected.commit();
+ }
+ }
+
+ /**
+ * Executes an unsafe command on the model. The editing domain may or may not already have an active transaction, which may or may not be
+ * read-only; it does not matter. In any case, the changes performed will not be recorded for undo/redo or roll-back. Thus, this is appropriate
+ * only for use cases such as synchronization of canonical views, which are not considered logically as abstract model edits (though they be
+ * concrete changes).
+ *
+ * @param domain
+ * an editing domain that may or may not have a transaction in progress
+ * @param command
+ * a command that will make unchecked/unsafe changes to the editing {@code domain}
+ *
+ * @throws RollbackException
+ * if the unprotected write transaction fails to commit. Note that this could occlude an uncaught exception thrown by the {@code writeOperation} runnable
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting for the unprotected write transaction to start
+ *
+ * @see #write(TransactionalEditingDomain, Runnable)
+ */
+ public static void write(TransactionalEditingDomain domain, Command command) throws InterruptedException, RollbackException {
+ write(domain, new CommandRunnable(command));
+ }
+
+ /**
+ * Executes an unsafe command on the model. The editing domain may or may not already have an active transaction, which may or may not be
+ * read-only; it does not matter. In any case, the changes performed will not be recorded for undo/redo or roll-back. Thus, this is appropriate
+ * only for use cases such as synchronization of canonical views, which are not considered logically as abstract model edits (though they be
+ * concrete changes).
+ *
+ * @param domain
+ * an editing domain that may or may not have a transaction in progress
+ * @param command
+ * a command that will make unchecked/unsafe changes to the editing {@code domain}
+ *
+ * @throws RollbackException
+ * if the unprotected write transaction fails to commit. Note that this could occlude an uncaught exception thrown by the {@code writeOperation} runnable
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting for the unprotected write transaction to start
+ * @throws ExecutionException
+ * if the {@code command} fails to execute
+ *
+ * @see #write(TransactionalEditingDomain, Runnable)
+ */
+ public static void write(TransactionalEditingDomain domain, ICommand command) throws InterruptedException, RollbackException, ExecutionException {
+ try {
+ write(domain, new GMFCommandRunnable(command));
+ } catch (WrappedException e) {
+ if (e.exception() instanceof ExecutionException) {
+ throw (ExecutionException) e.exception();
+ } else {
+ // It must have been an unchecked RuntimeException of some kind
+ throw (RuntimeException) e.exception();
+ }
+ }
+ }
+
+ /**
+ * Wraps a command for unprotected execution, undo, and redo on the command stack.
+ *
+ * @param domain
+ * a transactional editing domain on which the {@code command} operates
+ * @param command
+ * a command to wrap
+ * @return the wrapped command
+ */
+ public static Command wrap(TransactionalEditingDomain domain, Command command) {
+ return new UnsafeCommandWrapper(domain, command);
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A useful base class for commands that need to execute, undo, and redo in unprotected mode on the command stack.
+ */
+ public static abstract class UnsafeCommand extends AbstractCommand {
+
+ private final TransactionalEditingDomain domain;
+
+ protected UnsafeCommand(TransactionalEditingDomain domain) {
+ this.domain = domain;
+ }
+
+ protected UnsafeCommand(TransactionalEditingDomain domain, String label, String description) {
+ super(label, description);
+
+ this.domain = domain;
+ }
+
+ protected UnsafeCommand(TransactionalEditingDomain domain, String label) {
+ super(label);
+
+ this.domain = domain;
+ }
+
+ @Override
+ public final void execute() {
+ try {
+ runUnprotected(domain, new Runnable() {
+
+ @Override
+ public void run() {
+ doExecute();
+ }
+ });
+ } catch (Exception e) {
+ handleException(e);
+ }
+ }
+
+ protected abstract void doExecute();
+
+ @Override
+ public final void undo() {
+ try {
+ runUnprotected(domain, new Runnable() {
+
+ @Override
+ public void run() {
+ doUndo();
+ }
+ });
+ } catch (Exception e) {
+ handleException(e);
+ }
+ }
+
+ protected void doUndo() {
+ // Pass. Usually, unprotected changes are not undoable
+ }
+
+ @Override
+ public final void redo() {
+ try {
+ runUnprotected(domain, new Runnable() {
+
+ @Override
+ public void run() {
+ doRedo();
+ }
+ });
+ } catch (Exception e) {
+ handleException(e);
+ }
+ }
+
+ protected void doRedo() {
+ // Pass. Usually, unprotected changes are not undoable
+ }
+
+ void handleException(Exception e) {
+ Activator.log.error(e);
+ }
+ }
+
+ private static class UnsafeCommandWrapper extends UnsafeCommand {
+
+ private final Command command;
+
+ UnsafeCommandWrapper(TransactionalEditingDomain domain, Command command) {
+ super(domain, command.getLabel(), command.getDescription());
+
+ this.command = command;
+ }
+
+ @Override
+ public void dispose() {
+ command.dispose();
+ }
+
+ @Override
+ public boolean canExecute() {
+ return command.canExecute();
+ }
+
+ @Override
+ protected void doExecute() {
+ command.execute();
+ }
+
+ @Override
+ public boolean canUndo() {
+ return command.canUndo();
+ }
+
+ @Override
+ protected void doUndo() {
+ command.undo();
+ }
+
+ @Override
+ protected void doRedo() {
+ command.redo();
+ }
+
+ @Override
+ public Collection<?> getAffectedObjects() {
+ return command.getAffectedObjects();
+ }
+
+ @Override
+ public Collection<?> getResult() {
+ return command.getResult();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Unsafe(%s)", command.toString()); //$NON-NLS-1$
+ }
+ }
+
+ private static class CommandRunnable implements Runnable {
+
+ private final Command command;
+
+ CommandRunnable(Command command) {
+ this.command = command;
+ }
+
+ @Override
+ public void run() {
+ command.execute();
+ }
+ }
+
+ private static class GMFCommandRunnable implements Runnable {
+
+ private final ICommand command;
+
+ GMFCommandRunnable(ICommand command) {
+ this.command = command;
+ }
+
+ @Override
+ public void run() {
+ try {
+ command.execute(new NullProgressMonitor(), null);
+ } catch (ExecutionException e) {
+ throw new WrappedException(e);
+ }
+ }
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationHistoryDirtyState.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationHistoryDirtyState.java
new file mode 100644
index 00000000000..1bbfd926c5a
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationHistoryDirtyState.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.emf.gmf.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.emf.common.command.BasicCommandStack;
+import org.eclipse.papyrus.infra.emf.gmf.command.INonDirtying;
+
+
+/**
+ * A utility class for tracking the dirty state of an operation history. It works in the
+ * same fashion as the EMF {@link BasicCommandStack}, accounting for operations that {@linkplain INonDirtying do not dirty} the editor.
+ */
+public class OperationHistoryDirtyState {
+
+ private static final Map<IUndoContext, OperationHistoryDirtyState> instances = new HashMap<IUndoContext, OperationHistoryDirtyState>();
+
+ private final AtomicInteger refCount = new AtomicInteger(0);
+
+ private final IUndoContext context;
+
+ private final IOperationHistory history;
+
+ private IOperationHistoryListener listener;
+
+ private IUndoableOperation savepoint;
+
+ private boolean forceDirty;
+
+ protected OperationHistoryDirtyState(IUndoContext context, IOperationHistory history) {
+ this.context = context;
+ this.history = history;
+
+ history.addOperationHistoryListener(createOperationHistoryListener());
+ }
+
+ /**
+ * Create a delegating dirty state, usually for legacy compatibility.
+ *
+ * @param delegate
+ * the real dirty state
+ */
+ OperationHistoryDirtyState(OperationHistoryDirtyState delegate) {
+ this.context = delegate.context;
+ this.history = delegate.history;
+ }
+
+ /**
+ * Obtains a new operation history dirty-state tracker. Every result of this call must eventually be {@linkplain #dispose() disposed},
+ * even if it is actually the same instance as returned by an earlier call, because instances are reference-counted.
+ */
+ public static OperationHistoryDirtyState newInstance(IUndoContext context, IOperationHistory history) {
+ return getInstance(context, history, OperationHistoryDirtyState::new);
+ }
+
+ protected static OperationHistoryDirtyState getInstance(IUndoContext context, IOperationHistory history, BiFunction<? super IUndoContext, ? super IOperationHistory, ? extends OperationHistoryDirtyState> factory) {
+ OperationHistoryDirtyState result;
+
+ synchronized (instances) {
+ result = instances.get(context);
+ if (result == null) {
+ result = factory.apply(context, history);
+ instances.put(context, result);
+ }
+ }
+
+ result.retain();
+ return result;
+ }
+
+ private IOperationHistoryListener createOperationHistoryListener() {
+ this.listener = new IOperationHistoryListener() {
+
+ @Override
+ public void historyNotification(OperationHistoryEvent event) {
+ switch (event.getEventType()) {
+ case OperationHistoryEvent.DONE:
+ case OperationHistoryEvent.UNDONE:
+ case OperationHistoryEvent.REDONE:
+ case OperationHistoryEvent.OPERATION_CHANGED:
+ // Check on our savepoint, if any
+ if ((savepoint != null) && !savepoint.hasContext(context)) {
+ // Our savepoint has been removed from the context (our undo/redo stack), so it
+ // is effectively lost
+ savepoint = null;
+ }
+ break;
+ case OperationHistoryEvent.OPERATION_REMOVED:
+ IUndoableOperation removed = event.getOperation();
+ if (removed != null) {
+ if (removed == savepoint) {
+ // the savepoint was removed, so now we can never return to it
+ savepoint = null;
+ } else if ((savepoint == null) && removed.hasContext(context) && !OperationUtils.isNonDirtying(removed)) {
+ // A dirtying operation has been lost from the history, so we will not now be able to return
+ // to a state equivalent to the savepoint
+ forceDirty = true;
+ }
+ }
+ break;
+ }
+ }
+ };
+
+ return this.listener;
+ }
+
+ private OperationHistoryDirtyState retain() {
+ refCount.incrementAndGet();
+ return this;
+ }
+
+ private boolean release() {
+ return refCount.decrementAndGet() == 0;
+ }
+
+ public void dispose() {
+ synchronized (instances) {
+ if (release()) {
+ instances.remove(context);
+
+ if (listener != null) {
+ history.removeOperationHistoryListener(listener);
+ listener = null;
+ }
+
+ savepoint = null;
+ }
+ }
+ }
+
+ public boolean isDirty() {
+ return forceDirty || OperationUtils.isDirty(history.getUndoHistory(context), history.getRedoHistory(context), savepoint);
+ }
+
+ public void saved() {
+ this.savepoint = history.getUndoOperation(context);
+ this.forceDirty = false;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof Delegator) ? equals(((Delegator) obj).delegate) : super.equals(obj);
+ }
+
+ //
+ // Nested types
+ //
+
+ public static class Delegator extends OperationHistoryDirtyState {
+ private final OperationHistoryDirtyState delegate;
+
+ protected Delegator(IUndoContext context, IOperationHistory history) {
+ this(newInstance(context, history));
+ }
+
+ protected Delegator(OperationHistoryDirtyState delegate) {
+ super(delegate);
+
+ this.delegate = delegate;
+
+ // And replace it
+ synchronized (instances) {
+ instances.put(delegate.context, this);
+ }
+ }
+
+ @Override
+ public boolean isDirty() {
+ return delegate.isDirty();
+ }
+
+ @Override
+ public void dispose() {
+ delegate.dispose();
+ }
+
+ @Override
+ public void saved() {
+ delegate.saved();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationUtils.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationUtils.java
new file mode 100644
index 00000000000..7878f6155ca
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/gmf/util/OperationUtils.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.emf.gmf.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.workspace.EMFCommandOperation;
+import org.eclipse.papyrus.infra.emf.gmf.command.EMFtoGMFCommandWrapper;
+import org.eclipse.papyrus.infra.emf.gmf.command.INonDirtying;
+
+
+/**
+ * Utilities for working with undoable operations.
+ */
+public class OperationUtils {
+
+ /**
+ * Not instantiable by clients.
+ */
+ private OperationUtils() {
+ super();
+ }
+
+ public static boolean anyDirtying(IUndoableOperation[] undoHistory) {
+ boolean result = false;
+
+ if ((undoHistory != null) && (undoHistory.length > 0)) {
+ for (int i = 0; i < undoHistory.length; i++) {
+ if (!isNonDirtying(undoHistory[i])) {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Queries whether an operation is non-dirtying. The only known non-dirtying operations, currently, are those that wrap a {@link AbstractCommand.NonDirtying}.
+ *
+ * @param operation
+ * an undoable operation
+ *
+ * @return whether it is a non-dirtying operation
+ */
+ public static boolean isNonDirtying(IUndoableOperation operation) {
+ boolean result = operation instanceof INonDirtying;
+ if (!result) {
+ Command command = unwrap(operation);
+ result = command instanceof AbstractCommand.NonDirtying;
+ }
+ return result;
+ }
+
+ /**
+ * Obtains the singular EMF {@link Command} that is wrapped by an {@code operation}, if it is a command wrapper of some kind.
+ *
+ * @param operation
+ * an operation
+ *
+ * @return the {@link Command} that it wraps, or {@code null} if it does not wrap a singular EMF command
+ */
+ public static Command unwrap(IUndoableOperation operation) {
+ Command result = null;
+
+ if (operation instanceof EMFCommandOperation) {
+ result = ((EMFCommandOperation) operation).getCommand();
+ } else if (operation instanceof EMFtoGMFCommandWrapper) {
+ result = ((EMFtoGMFCommandWrapper) operation).getWrappedCommand();
+ }
+
+ return result;
+ }
+
+ public static boolean isDirty(IUndoableOperation[] undoHistory, IUndoableOperation[] redoHistory, IUndoableOperation savepoint) {
+ boolean result = false;
+
+ if (savepoint == null) {
+ result = anyDirtying(undoHistory);
+ } else {
+ List<IUndoableOperation> undos = ((undoHistory == null) || (undoHistory.length == 0)) ? Collections.<IUndoableOperation> emptyList() : Arrays.asList(undoHistory);
+ List<IUndoableOperation> redos = ((redoHistory == null) || (redoHistory.length == 0)) ? Collections.<IUndoableOperation> emptyList() : Arrays.asList(redoHistory);
+
+ if (undos.contains(savepoint)) {
+ // See whether there is any dirtying command after the savepoint in the undo stack
+ int i = 0;
+
+ for (; i < undoHistory.length; i++) {
+ if (undoHistory[i] == savepoint) {
+ i++; // Advance over the save point to start testing
+ break;
+ }
+ }
+
+ for (; i < undoHistory.length; i++) {
+ if (!isNonDirtying(undoHistory[i])) {
+ result = true;
+ break;
+ }
+ }
+ } else if (redos.contains(savepoint)) {
+ // See whether there is any dirtying command before the savepoint in the redo stack
+ for (int i = redoHistory.length - 1; i >= 0; i--) {
+ if (!isNonDirtying(redoHistory[i])) {
+ result = true;
+ break;
+ }
+ if (redoHistory[i] == savepoint) {
+ // Done scanning. Everything up to and including the savepoint is non-dirtying
+ break;
+ }
+ }
+ } else {
+ // If we have no history but we have a savepoint, then we cannot undo nor redo to that savepoint
+ // (the history has been flushed) so evidently some change was made that invalidated the history,
+ // therefore we are dirty
+ result = true;
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/internal/gmf/Activator.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/internal/gmf/Activator.java
new file mode 100644
index 00000000000..cffec0820a5
--- /dev/null
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf.gmf/src/org/eclipse/papyrus/infra/emf/internal/gmf/Activator.java
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Copyright (c) 2013, 2016 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Camille Letavernier (camille.letavernier@cea.fr) - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.emf.internal.gmf;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.papyrus.infra.core.log.LogHelper;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends Plugin {
+
+ /**
+ * The plug-in ID
+ */
+ public static final String PLUGIN_ID = "org.eclipse.papyrus.infra.emf,gmf"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The plug-in's logger
+ */
+ public static LogHelper log;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ super();
+ }
+
+ @Override
+ public void start(final BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ log = new LogHelper(this);
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}

Back to the top