| /********************************************************************* |
| * Copyright (c) 2008 The University of York. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| **********************************************************************/ |
| package org.eclipse.epsilon.picto.source; |
| |
| import java.io.File; |
| import java.net.URI; |
| import java.util.*; |
| import java.util.stream.Collectors; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.epsilon.common.dt.console.EpsilonConsole; |
| import org.eclipse.epsilon.common.dt.launching.extensions.ModelTypeExtension; |
| import org.eclipse.epsilon.common.dt.util.LogUtil; |
| import org.eclipse.epsilon.common.util.StringProperties; |
| import org.eclipse.epsilon.common.util.UriUtil; |
| import org.eclipse.epsilon.egl.EglFileGeneratingTemplateFactory; |
| import org.eclipse.epsilon.egl.EglTemplateFactoryModuleAdapter; |
| import org.eclipse.epsilon.emc.emf.InMemoryEmfModel; |
| import org.eclipse.epsilon.eol.IEolModule; |
| import org.eclipse.epsilon.eol.dt.ExtensionPointToolNativeTypeDelegate; |
| import org.eclipse.epsilon.eol.execute.context.FrameStack; |
| import org.eclipse.epsilon.eol.execute.context.IEolContext; |
| import org.eclipse.epsilon.eol.execute.context.Variable; |
| import org.eclipse.epsilon.eol.models.IModel; |
| import org.eclipse.epsilon.eol.models.IRelativePathResolver; |
| import org.eclipse.epsilon.eol.types.EolAnyType; |
| import org.eclipse.epsilon.picto.Layer; |
| import org.eclipse.epsilon.picto.LazyEgxModule; |
| import org.eclipse.epsilon.picto.LazyEgxModule.LazyGenerationRule; |
| import org.eclipse.epsilon.picto.LazyEgxModule.LazyGenerationRuleContentPromise; |
| import org.eclipse.epsilon.picto.ResourceLoadingException; |
| import org.eclipse.epsilon.picto.StaticContentPromise; |
| import org.eclipse.epsilon.picto.ViewTree; |
| import org.eclipse.epsilon.picto.dom.*; |
| import org.eclipse.ui.IEditorPart; |
| |
| public abstract class EglPictoSource implements PictoSource { |
| |
| protected Collection<IModel> models = new ArrayList<>(); |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public ViewTree getViewTree(IEditorPart editor) throws Exception { |
| |
| IFile iFile = waitForFile(editor); |
| if (iFile == null) return createEmptyViewTree(); |
| |
| File modelFile = new File(iFile.getLocation().toOSString()); |
| Resource resource; |
| ViewTree viewTree = new ViewTree(); |
| |
| try { |
| resource = getResource(editor); |
| } |
| catch (Exception ex) { |
| throw new ResourceLoadingException(ex); |
| } |
| |
| Picto renderingMetadata = getRenderingMetadata(editor); |
| |
| if (renderingMetadata != null) { |
| IEolModule module; |
| IModel model = null; |
| |
| //if (renderingMetadata.getNsuri() != null) { |
| // EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(renderingMetadata.getNsuri()); |
| // model = new InMemoryEmfModel("M", resource, ePackage); |
| //} |
| //else { |
| if (resource != null) { |
| model = new InMemoryEmfModel("M", resource); |
| ((InMemoryEmfModel) model).setExpand(true); |
| } |
| //} |
| |
| if (renderingMetadata.getFormat() == null) renderingMetadata.setFormat("egx"); |
| |
| if ("egx".equals(renderingMetadata.getFormat())) { |
| module = new LazyEgxModule(); |
| } |
| else { |
| module = new EglTemplateFactoryModuleAdapter(new EglFileGeneratingTemplateFactory()); |
| } |
| |
| IEolContext context = module.getContext(); |
| context.getNativeTypeDelegates().add(new ExtensionPointToolNativeTypeDelegate()); |
| |
| FrameStack fs = context.getFrameStack(); |
| for (Parameter customParameter : renderingMetadata.getParameters()) { |
| fs.put(new Variable(customParameter.getName(), getValue(customParameter), EolAnyType.Instance)); |
| } |
| |
| URI transformationUri = null; |
| |
| if (renderingMetadata.getTransformation() != null) { |
| transformationUri = UriUtil.resolve(renderingMetadata.getTransformation(), modelFile.toURI()); |
| module.parse(transformationUri); |
| } |
| else { |
| module.parse(""); |
| } |
| |
| context.setOutputStream(EpsilonConsole.getInstance().getDebugStream()); |
| context.setErrorStream(EpsilonConsole.getInstance().getErrorStream()); |
| context.setWarningStream(EpsilonConsole.getInstance().getWarningStream()); |
| if (model != null) context.getModelRepository().addModel(model); |
| |
| for (Model pictoModel : renderingMetadata.getModels()) { |
| model = loadModel(pictoModel, modelFile); |
| if (model != null) models.add(model); |
| } |
| |
| context.getModelRepository().addModels(models); |
| |
| if ("egx".equals(renderingMetadata.getFormat())) { |
| |
| List<LazyGenerationRuleContentPromise> instances = (List<LazyGenerationRuleContentPromise>) module.execute(); |
| |
| // Handle dynamic views (i.e. where type != null) |
| for (CustomView customView : renderingMetadata.getCustomViews().stream().filter(cv -> cv.getType() != null).collect(Collectors.toList())) { |
| |
| LazyGenerationRule generationRule = ((LazyEgxModule) module).getGenerationRules().stream() |
| .filter(r -> r.getName().equals(customView.getType()) && r instanceof LazyGenerationRule) |
| .map(LazyGenerationRule.class::cast) |
| .findFirst().orElse(null); |
| |
| if (generationRule != null) { |
| Object source = null; |
| if (generationRule.getSourceParameter() != null) { |
| String sourceParameterName = generationRule.getSourceParameter().getName(); |
| Parameter sourceParameter = customView.getParameters() |
| .stream() |
| .filter(sp -> sp.getName().equals(sourceParameterName)) |
| .findFirst().orElse(null); |
| if (sourceParameter != null) { |
| customView.getParameters().remove(sourceParameter); |
| source = sourceParameter.getValue(); |
| } |
| } |
| |
| if (customView.getPath() != null) customView.getParameters().add(createParameter("path", customView.getPath())); |
| if (customView.getIcon() != null) customView.getParameters().add(createParameter("icon", customView.getIcon())); |
| if (customView.getFormat() != null) customView.getParameters().add(createParameter("format", customView.getFormat())); |
| customView.getParameters().add(createParameter("patches", customView.getPatches())); |
| if (customView.getPosition() != null) customView.getParameters().add(createParameter("position", customView.getPosition())); |
| if (customView.eIsSet(PictoPackage.eINSTANCE.getCustomView_Layers())) { |
| customView.getParameters().add(createParameter("activeLayers", customView.getLayers())); |
| } |
| |
| |
| for (Parameter customViewParameter : customView.getParameters()) { |
| fs.put(new Variable(customViewParameter.getName(), getValue(customViewParameter), EolAnyType.Instance)); |
| } |
| |
| LazyGenerationRuleContentPromise contentPromise = (LazyGenerationRuleContentPromise) |
| generationRule.execute(context, source); |
| |
| Collection<Variable> variables = contentPromise.getVariables(); |
| |
| for (Parameter parameter : customView.getParameters()) { |
| Object value = getValue(parameter); |
| String paramName = parameter.getName(); |
| |
| Variable variable = variables.stream() |
| .filter(v -> v.getName().equals(paramName)) |
| .findAny() |
| .orElse(null); |
| |
| if (variable != null) { |
| variable.setValue(value, context); |
| } |
| else { |
| variables.add(new Variable(paramName, value, EolAnyType.Instance, false)); |
| } |
| } |
| instances.add(contentPromise); |
| } |
| } |
| |
| for (LazyGenerationRuleContentPromise instance : instances) { |
| String format = getDefaultFormat(); |
| String icon = getDefaultIcon(); |
| List<Patch> patches = new ArrayList<>(1); |
| Collection<String> path = Arrays.asList(""); |
| List<Layer> layers = new ArrayList<>(); |
| Variable layersVariable = null; |
| Integer position = null; |
| |
| Collection<Variable> instanceVariables = instance.getVariables(); |
| |
| for (Variable variable : instanceVariables) { |
| Object varValue = variable.getValue(); |
| switch (variable.getName()) { |
| case "format": { |
| format = varValue + ""; |
| break; |
| } |
| case "path": { |
| if (!(varValue instanceof Collection)) { |
| (path = (Collection<String>) (varValue = new ArrayList<>(1))) |
| .add(Objects.toString(varValue)); |
| } |
| else if (!((Collection<?>) varValue).isEmpty()) { |
| path = ((Collection<?>) varValue).stream() |
| .map(Objects::toString).collect(Collectors.toList()); |
| } |
| break; |
| } |
| case "icon": { |
| icon = varValue + ""; |
| break; |
| } |
| case "position": { |
| if (varValue instanceof Integer) { |
| position = (Integer) varValue; |
| } |
| else if (varValue != null) { |
| position = Integer.parseInt(varValue.toString()); |
| } |
| break; |
| } |
| case "layers": { |
| layersVariable = variable; |
| for (Object layerMapObject : (Iterable<?>) varValue) { |
| Map<Object, Object> layerMap = (Map<Object, Object>) layerMapObject; |
| Layer layer = new Layer(); |
| layer.setId(layerMap.get("id") + ""); |
| layer.setTitle(layerMap.get("title") + ""); |
| if (layerMap.containsKey("active")) { |
| layer.setActive((boolean) layerMap.get("active")); |
| } |
| layers.add(layer); |
| } |
| break; |
| } |
| case "patches": { |
| if (varValue instanceof List) { |
| patches = (List<Patch>) varValue; |
| } |
| else if (varValue instanceof Patch) { |
| patches.add((Patch) varValue); |
| } |
| else if (varValue instanceof Collection) { |
| patches.addAll((Collection<? extends Patch>) varValue); |
| } |
| break; |
| } |
| } |
| |
| } |
| |
| // If this is a custom view there may be an activeLayers variable in the variables list |
| Variable activeLayersVariable = instanceVariables.stream().filter(v -> v.getName().equals("activeLayers")).findAny().orElse(null); |
| if (activeLayersVariable != null) { |
| Collection<?> activeLayers = (Collection<?>) activeLayersVariable.getValue(); |
| for (Layer layer : layers) { |
| layer.setActive(activeLayers.contains(layer.getId())); |
| } |
| } |
| |
| // Replace layers variable from list of maps to list of Layer objects |
| if (layersVariable != null) { |
| instanceVariables.remove(layersVariable); |
| } |
| instanceVariables.add(Variable.createReadOnlyVariable("layers", layers)); |
| viewTree.add(new ArrayList<>(path), new ViewTree(instance, format, icon, position, patches, layers)); |
| } |
| |
| } |
| else { |
| String content = module.execute() + ""; |
| viewTree = new ViewTree(); |
| viewTree.setPromise(new StaticContentPromise(content)); |
| viewTree.setFormat(renderingMetadata.getFormat()); |
| } |
| |
| // Handle static views (i.e. where source != null) |
| for (CustomView customView : renderingMetadata.getCustomViews().stream().filter(cv -> cv.getSource() != null).collect(Collectors.toList())) { |
| String format = customView.getFormat() != null ? customView.getFormat() : getDefaultFormat(); |
| String icon = customView.getIcon() != null ? customView.getIcon() : getDefaultIcon(); |
| |
| viewTree.add(customView.getPath(), new ViewTree(new StaticContentPromise(new File(new File(customView.eResource().getURI().toFileString()).getParentFile(), customView.getSource())), |
| format, icon, customView.getPosition(), customView.getPatches(), Collections.emptyList())); |
| } |
| |
| // Handle patches for existing views (i.e. where source == null and type/rule == null) |
| for (CustomView customView : renderingMetadata.getCustomViews().stream().filter(cv -> cv.getSource() == null && cv.getType() == null).collect(Collectors.toList())) { |
| ArrayList<String> path = new ArrayList<>(); |
| path.add(viewTree.getName()); |
| path.addAll(customView.getPath()); |
| |
| ViewTree existingView = viewTree.forPath(path); |
| |
| if (existingView != null) { |
| if (customView.getIcon() != null) existingView.setIcon(customView.getIcon()); |
| if (customView.getFormat() != null) existingView.setFormat(customView.getFormat()); |
| if (customView.getPosition() != null) existingView.setPosition(customView.getPosition()); |
| |
| existingView.getPatches().addAll(customView.getPatches()); |
| if (customView.eIsSet(PictoPackage.eINSTANCE.getCustomView_Layers())) { |
| Collection<?> layers = customView.getLayers(); |
| for (Layer layer : existingView.getLayers()) { |
| layer.setActive(layers.contains(layer.getId())); |
| } |
| } |
| } |
| } |
| |
| if (transformationUri != null) { |
| viewTree.getBaseUris().add(transformationUri); |
| viewTree.getBaseUris().add(transformationUri.resolve("./icons/")); |
| } |
| |
| viewTree.getBaseUris().add(new URI(modelFile.toURI().toString())); |
| |
| return viewTree; |
| } |
| else { |
| return createEmptyViewTree(); |
| } |
| |
| } |
| |
| protected Parameter createParameter(String name, Object value) { |
| Parameter parameter = PictoFactory.eINSTANCE.createParameter(); |
| parameter.setName(name); |
| parameter.setValue(value); |
| return parameter; |
| } |
| |
| protected IFile waitForFile(IEditorPart editorPart) { |
| // TODO FIXME : Why is this not using wait / notify mechanism? |
| int attempts = 0; |
| int maxAttempts = 50; |
| IFile file = getFile(editorPart); |
| while (file == null && attempts < maxAttempts) { |
| try { Thread.sleep(100); } catch (InterruptedException e) {} |
| file = getFile(editorPart); |
| attempts++; |
| } |
| return file; |
| } |
| |
| protected ViewTree createEmptyViewTree() { |
| ViewTree viewTree = new ViewTree(); |
| viewTree.setPromise(new StaticContentPromise("Nothing to render")); |
| viewTree.setFormat("text"); |
| return viewTree; |
| } |
| |
| @Override |
| public void dispose() { |
| for (IModel model : models) { |
| try { |
| model.dispose(); |
| } |
| catch (Exception ex) { |
| LogUtil.log(ex); |
| } |
| } |
| models.clear(); |
| } |
| |
| protected IModel loadModel(Model model, File baseFile) throws Exception { |
| IModel m = ModelTypeExtension.forType(model.getType()).createModel(); |
| m.setName(model.getName()); |
| m.setReadOnLoad(true); |
| m.setStoredOnDisposal(false); |
| StringProperties properties = new StringProperties(); |
| IRelativePathResolver relativePathResolver = relativePath -> |
| new File(baseFile.getParentFile(), relativePath).getAbsolutePath(); |
| |
| for (Parameter parameter : model.getParameters()) { |
| properties.put(parameter.getName(), parameter.getFile() != null ? |
| relativePathResolver.resolve(parameter.getFile()) : parameter.getValue() |
| ); |
| } |
| m.load(properties, relativePathResolver); |
| return m; |
| } |
| |
| protected String getDefaultFormat() { |
| return "html"; |
| } |
| |
| protected String getDefaultIcon() { |
| return "diagram-cccccc"; |
| } |
| |
| @Override |
| public boolean supports(IEditorPart editorPart) { |
| return supportsEditorType(editorPart) && getRenderingMetadata(editorPart) != null; |
| } |
| |
| public Object getValue(Parameter parameter) { |
| Object value = parameter.getValue(); |
| if (value == null && parameter.eIsSet(PictoPackage.Literals.PARAMETER__VALUES)) { |
| value = parameter.getValues(); |
| } |
| if (value == null && !parameter.getItems().isEmpty()) { |
| // If one of the nested items has a name, then the parameter is a Map |
| if (parameter.getItems().stream().anyMatch(item -> item.getName() != null)) { |
| Map<String, Object> values = new LinkedHashMap<>(); |
| for (Parameter item : parameter.getItems()) { |
| String key = item.getName(); |
| if (key == null) key = ""; |
| values.put(key, getValue(item)); |
| } |
| value = values; |
| } |
| else { |
| value = parameter.getItems().stream() |
| .map(item -> getValue(item)) |
| .collect(Collectors.toCollection(ArrayList::new)); |
| } |
| } |
| return value; |
| } |
| |
| protected abstract Picto getRenderingMetadata(IEditorPart editorPart); |
| |
| protected abstract Resource getResource(IEditorPart editorPart); |
| |
| protected abstract IFile getFile(IEditorPart editorPart); |
| |
| protected abstract boolean supportsEditorType(IEditorPart editorPart); |
| } |