diff --git a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/V4MDSpecificEnvironmentOptionsGroup.java b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/V4MDSpecificEnvironmentOptionsGroup.java index 3d73507..bad6aae 100644 --- a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/V4MDSpecificEnvironmentOptionsGroup.java +++ b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/V4MDSpecificEnvironmentOptionsGroup.java @@ -29,9 +29,14 @@ public class V4MDSpecificEnvironmentOptionsGroup extends AbstractPropertyOptionsGroup implements EnvironmentChangeListener { private static final String V4MD_DEVELOPER_MODE_REQUIRED = "Developer Mode is required. "; + private static final String V4MD_GROUP_ADVANCED_NAME = "Advanced"; private static final String V4MD_GROUP_DEBUGGING_NAME = "Debugging"; private static final String V4MD_GROUP_ID = "V4MD"; private static final String V4MD_GROUP_NAME = "V4MD"; + public static final String INDEX_DIAGRAM_ID = "INDEX_DIAGRAM_ID"; + private static final String INDEX_DIAGRAM_NAME = "Enable Diagram Content Indexing"; + private static final String INDEX_DIAGRAM_DESCRIPTION = "By default, contents of the diagrams are not indexed. When diagram content indexing is enabled, diagram related queries can be defined. However, performance issues may occur due to the fact that more elements need to be indexed. After changing the property, you have to reload the currently open projects."; + private static final String INDEX_DIAGRAM_DESCRIPTION_ID = "INDEX_DIAGRAM_DESCRIPTION_ID"; public static final String USE_EMPTY_QUERY_SCOPE_ID = "USE_EMPTY_QUERY_SCOPE"; private static final String USE_EMPTY_QUERY_SCOPE_NAME = "Disable Model Indexing"; private static final String USE_EMPTY_QUERY_SCOPE_DESCRIPTION = "For debugging purposes, this property disables model indexing by V4MD and all queries will return an empty result. After changing the property, you have to reload the currently open projects."; @@ -77,7 +82,8 @@ public void loadOptions(Style style, boolean paramBoolean) { @Override public void setDefaultValues() { super.setDefaultValues(); - createUseEmptyQueryScope(false); + createDiagramContentIndexingProperty(false); + createUseEmptyQueryScopeProperty(false); setEditability(getProperty(USE_EMPTY_QUERY_SCOPE_ID)); } @@ -100,7 +106,7 @@ public boolean isEmptyQueryScopeRequired() { return returnValue; } - public void createUseEmptyQueryScope(boolean value) { + public void createUseEmptyQueryScopeProperty(boolean value) { Property emptyScopeProperty = new BooleanProperty(USE_EMPTY_QUERY_SCOPE_ID, value); emptyScopeProperty.setGroup(V4MD_GROUP_DEBUGGING_NAME); emptyScopeProperty.setResourceProvider(new PropertyResourceProvider() { @@ -117,6 +123,38 @@ public String getString(String key, Property property) { addProperty(emptyScopeProperty, USE_EMPTY_QUERY_SCOPE_DESCRIPTION_ID); } + @Used + public boolean isDiagramContentIndexingEnabled() { + + Property p = getProperty(INDEX_DIAGRAM_ID); + if (p == null) { + logger.info("Diagram Content Indexing property is not yet set."); + return false; // This property is not yet set. + } + + Boolean returnValue = (Boolean) p.getValue(); + logger.info("Diagram Content Indexing property is currently set to " + returnValue.toString() + "."); + return returnValue; + } + + public void createDiagramContentIndexingProperty(boolean value) { + Property emptyScopeProperty = new BooleanProperty(INDEX_DIAGRAM_ID, value); + emptyScopeProperty.setGroup(V4MD_GROUP_ADVANCED_NAME); + emptyScopeProperty.setResourceProvider(new PropertyResourceProvider() { + + @Override + public String getString(String key, Property property) { + if (Objects.equals(INDEX_DIAGRAM_ID, key)) + return INDEX_DIAGRAM_NAME; + if (Objects.equals(INDEX_DIAGRAM_DESCRIPTION_ID, key)) + return INDEX_DIAGRAM_DESCRIPTION; + return key; + } + }); + addProperty(emptyScopeProperty, INDEX_DIAGRAM_DESCRIPTION_ID); + logger.info("Diagram Content Indexing property is set to default."); + } + private void setEditability(@CheckForNull Property p) { if(p == null) return; diff --git a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/ViatraQueryAdapter.java b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/ViatraQueryAdapter.java index c88a466..3cc1dc1 100644 --- a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/ViatraQueryAdapter.java +++ b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/ViatraQueryAdapter.java @@ -331,6 +331,10 @@ private static MagicDrawProjectScope createMagicDrawProjectScope(Project project if(V4MDSpecificEnvironmentOptionsGroup.getCurrentGroup().isEmptyQueryScopeRequired()) { return MagicDrawProjectScope.createMagicDrawEmptyProjectScope(project); } - return new MagicDrawProjectScope(project, ViatraQueryAdapterOptions.getInstance().isEnableEngineProfiling(), notifiers); + + boolean enableDiagramContentIndexing = V4MDSpecificEnvironmentOptionsGroup.getCurrentGroup().isDiagramContentIndexingEnabled(); + boolean enableProfiler = ViatraQueryAdapterOptions.getInstance().isEnableEngineProfiling(); + LOGGER.info("Initializing MagicDrawProject Scope with parameters enableProfiler=" + enableProfiler +" enableDiagramContentIndexing=" + enableDiagramContentIndexing); + return new MagicDrawProjectScope(project, enableProfiler, enableDiagramContentIndexing, notifiers); } } diff --git a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/internal/MagicDrawProjectScope.java b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/internal/MagicDrawProjectScope.java index bbb8121..4f16a5a 100644 --- a/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/internal/MagicDrawProjectScope.java +++ b/com.incquerylabs.v4md/src/main/com/incquerylabs/v4md/internal/MagicDrawProjectScope.java @@ -31,55 +31,88 @@ public class MagicDrawProjectScope extends EMFScope { private List listeners = new ArrayList<>(); private boolean useEmptyQueryScope = false; - // XXX Omitting references can cause semantic errors (so far we are in the clear though) - // these references are only present in UML profiles, typically their contents are equal to the original references inherited from the UML type hierarchy, however there are some cases when this might not be the case. - private static final BaseIndexOptions BASE_OPTIONS = new BaseIndexOptions() - .withFeatureFilterConfiguration(reference -> reference instanceof EReference && isReferenceToBeFiltered((EReference) reference)) + // XXX Omitting references can cause semantic errors (so far we are in the clear + // though) + // these references are only present in UML profiles, typically their contents + // are equal to the original references inherited from the UML type hierarchy, + // however there are some cases when this might not be the case. + private static final BaseIndexOptions BASE_OPTIONS = new BaseIndexOptions().withFeatureFilterConfiguration( + reference -> reference instanceof EReference && isReferenceToBeFiltered((EReference) reference, false)) .withStrictNotificationMode(false); - private static final BaseIndexOptions BASE_OPTIONS_WITH_PROFILER = BASE_OPTIONS.withIndexProfilerMode(ProfilerMode.START_DISABLED); - - private static boolean isReferenceToBeFiltered(EReference reference) { + private static final BaseIndexOptions BASE_OPTIONS_ENABLE_DIAGRAM = new BaseIndexOptions() + .withFeatureFilterConfiguration( + reference -> reference instanceof EReference && isReferenceToBeFiltered((EReference) reference, true)) + .withStrictNotificationMode(false); + private static final BaseIndexOptions BASE_OPTIONS_WITH_PROFILER = BASE_OPTIONS + .withIndexProfilerMode(ProfilerMode.START_DISABLED); + private static final BaseIndexOptions BASE_OPTIONS_ENABLE_DIAGRAM_WITH_PROFILER = BASE_OPTIONS_ENABLE_DIAGRAM + .withIndexProfilerMode(ProfilerMode.START_DISABLED); + + private static boolean isReferenceToBeFiltered(EReference reference, boolean enableDiagramContentIndexing) { String name = reference.getName(); - return (reference.isContainment() && name.contains("_from_")) - || - name.startsWith("_"); + if (reference.isContainment() && name.contains("_from_")) { + return true; + } else if (enableDiagramContentIndexing && name.equals("_representation")) { + /* + * "_representation" is a special feature of the MagicDraw metamodel that + * describes the containment of diagram related representation elements + */ + return false; + } else if (name.startsWith("_")) { + return true; + } + return false; + } + + static BaseIndexOptions getBaseIndexOptions(boolean enableProfiler, boolean enableDiagramContentIndexing) { + if(enableProfiler) { + if(enableDiagramContentIndexing) + return BASE_OPTIONS_ENABLE_DIAGRAM_WITH_PROFILER; + return BASE_OPTIONS_WITH_PROFILER; + } else { + if(enableDiagramContentIndexing) + return BASE_OPTIONS_ENABLE_DIAGRAM; + return BASE_OPTIONS; + } } static Stream getProjectModels(Project projectModel) { Package primaryModel = projectModel.getPrimaryModel(); return projectModel.getModels().stream().filter(pkg -> pkg == primaryModel || !EcoreUtil.isAncestor(primaryModel, pkg)); } - + static Stream getCustomNotifiers(Notifier... notifiers) { return Arrays.stream(notifiers); } - + /** * A special constructor that provides the ability to create empty scope. */ private MagicDrawProjectScope(Project project) { - super(new ResourceSetImpl(), BASE_OPTIONS); //Mocking a dummy notifier + super(new ResourceSetImpl(), BASE_OPTIONS); // Mocking a dummy notifier this.project = project; this.customNotifiers = new Notifier[0]; this.useEmptyQueryScope = true; } - + /** - * Returns a special empty project scope associated to the given project where only a dummy notifier is indexed. + * Returns a special empty project scope associated to the given project where + * only a dummy notifier is indexed. + * * @param project associated to the empty scope * @return a special empty project scope */ public static MagicDrawProjectScope createMagicDrawEmptyProjectScope(Project project) { return new MagicDrawProjectScope(project); } - + public MagicDrawProjectScope(Project project, Notifier... notifiers) { - this(project, false, notifiers); + this(project, false, false, notifiers); } - - public MagicDrawProjectScope(Project project, boolean enableProfiler, Notifier... notifiers) { + + public MagicDrawProjectScope(Project project, boolean enableProfiler, boolean enableDiagramContentIndexing, Notifier... notifiers) { super(Stream.concat(getProjectModels(project), getCustomNotifiers(notifiers)).collect(Collectors.toSet()), - enableProfiler ? BASE_OPTIONS_WITH_PROFILER : BASE_OPTIONS); + getBaseIndexOptions(enableProfiler, enableDiagramContentIndexing)); this.project = project; this.customNotifiers = notifiers; } @@ -93,25 +126,28 @@ protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexing Logger logger) { return new MagicDrawProjectEngineContext(this, errorListener, logger, useEmptyQueryScope); } - + public void projectStructureUpdated() { for (IProjectChangedListener listener : listeners) { listener.modelSetUpdated(); } } - + Stream getProjectModels() { return getProjectModels(project); } - + Stream getCustomNotifiers() { return getCustomNotifiers(customNotifiers); } - + public static MagicDrawProjectNavigationHelper extractNavigationHelper(ViatraQueryEngine engine) { final QueryScope scope = engine.getScope(); - if (scope instanceof MagicDrawProjectScope) - return (MagicDrawProjectNavigationHelper) ((EMFBaseIndexWrapper)AdvancedViatraQueryEngine.from(engine).getBaseIndex()).getNavigationHelper(); - else throw new IllegalArgumentException("Cannot extract EMF base index from VIATRA Query engine instantiated on non-EMF scope " + scope); + if (scope instanceof MagicDrawProjectScope) + return (MagicDrawProjectNavigationHelper) ((EMFBaseIndexWrapper) AdvancedViatraQueryEngine.from(engine) + .getBaseIndex()).getNavigationHelper(); + else + throw new IllegalArgumentException( + "Cannot extract EMF base index from VIATRA Query engine instantiated on non-EMF scope " + scope); } }