001package org.nasdanika.html.model.app.graph.emf; 002 003import java.io.File; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.LinkedHashMap; 008import java.util.Map; 009import java.util.Map.Entry; 010import java.util.function.Consumer; 011import java.util.function.Function; 012 013import org.eclipse.emf.common.util.URI; 014import org.eclipse.emf.ecore.EObject; 015import org.eclipse.emf.ecore.resource.Resource; 016import org.eclipse.emf.ecore.resource.ResourceSet; 017import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; 018import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; 019import org.nasdanika.common.Diagnostic; 020import org.nasdanika.common.ProgressMonitor; 021import org.nasdanika.common.Transformer; 022import org.nasdanika.graph.Connection; 023import org.nasdanika.graph.Element; 024import org.nasdanika.graph.emf.EObjectGraphFactory; 025import org.nasdanika.graph.emf.EObjectNode; 026import org.nasdanika.graph.processor.NopEndpointProcessorConfigFactory; 027import org.nasdanika.graph.processor.ProcessorConfig; 028import org.nasdanika.graph.processor.ProcessorInfo; 029import org.nasdanika.graph.processor.ReflectiveProcessorFactoryProvider; 030import org.nasdanika.graph.processor.emf.EObjectNodeProcessorReflectiveFactory; 031import org.nasdanika.html.model.app.Label; 032import org.nasdanika.html.model.app.Link; 033import org.nasdanika.html.model.app.graph.WidgetFactory; 034 035/** 036 * Base class for action generation using node processor fatory 037 * @param <F> Node processor factory type. 038 */ 039public class ActionGenerator<F> { 040 041 protected Collection<? extends EObject> sources; 042 protected F nodeProcessorFactory; 043 protected Collection<? extends EObject> references; 044 protected Function<? super EObject, URI> uriResolver; 045 046 public ActionGenerator( 047 Collection<? extends EObject> sources, 048 F nodeProcessorFactory, 049 Collection<? extends EObject> references, 050 Function<? super EObject, URI> uriResolver) { 051 052 this.sources = sources; 053 this.nodeProcessorFactory = nodeProcessorFactory; 054 this.references = references; 055 this.uriResolver = uriResolver; 056 } 057 058 /** 059 * Uses transformer and a reflective node processor factory to generate actions from sources 060 * @param sources Source objects 061 * @param nodeProcessorFactory Node processor factory 062 * @param references Objects which might be referenced by the sources and as such need their URI's resolved for proper linking 063 * @param uriResolver Resolver of URI's for sources and references 064 * @param diagnosticConsumer Diagnostic consumer 065 * @param progressMonitor Progress monitor 066 * @return A map of source objects to a collection of labels created from those objects 067 */ 068 public Map<EObject,Collection<Label>> generateActionModel(Consumer<Diagnostic> diagnosticConsumer, ProgressMonitor progressMonitor) { 069 Transformer<EObject,Element> graphFactory = new Transformer<>(createGraphFactory()); 070 Map<EObject, Element> graph = graphFactory.transform(sources, false, progressMonitor); 071 072 Object configFactory = createConfigFactory(); 073 074 Transformer<Element,ProcessorConfig> processorConfigTransformer = new Transformer<>(configFactory); 075 Map<Element, ProcessorConfig> configs = processorConfigTransformer.transform(graph.values(), false, progressMonitor); 076 077 Object reflectiveFactory = createReflectiveFactory(); 078 ReflectiveProcessorFactoryProvider<Object, WidgetFactory, WidgetFactory> eObjectReflectiveProcessorFactoryProvider = createReflectiveFactoryProvider(reflectiveFactory); 079 Map<Element, ProcessorInfo<Object>> registry = eObjectReflectiveProcessorFactoryProvider.getFactory().createProcessors(configs.values(), false, progressMonitor); 080 081 if (references != null) { 082 for (EObject reference: references) { 083 URI refURI = uriResolver.apply(reference); 084 if (refURI != null) { 085 for (Entry<Element, ProcessorInfo<Object>> re: registry.entrySet()) { 086 Element element = re.getKey(); 087 if (element instanceof EObjectNode) { 088 EObjectNode eObjNode = (EObjectNode) element; 089 EObject target = eObjNode.get(); 090 if (target == reference) { 091 ProcessorInfo<Object> info = re.getValue(); 092 Object processor = info.getProcessor(); 093 if (processor instanceof WidgetFactory) { 094 WidgetFactory widgetFactoryNodeProcessor = (WidgetFactory) processor; 095 widgetFactoryNodeProcessor.resolve(refURI, progressMonitor); 096 } 097 } 098 } 099 } 100 } 101 } 102 } 103 104 record SourceProcessorRecord(EObject source, URI uri, WidgetFactory widgetFactory) {} 105 106 Collection<SourceProcessorRecord> sourceProcessorRecords = new ArrayList<>(); 107 for (EObject source: sources) { 108 URI sourceURI = uriResolver.apply(source); 109 for (Entry<Element, ProcessorInfo<Object>> re: registry.entrySet()) { 110 Element element = re.getKey(); 111 if (element instanceof EObjectNode) { 112 EObjectNode eObjNode = (EObjectNode) element; 113 EObject target = eObjNode.get(); 114 if (target == source) { 115 ProcessorInfo<Object> info = re.getValue(); 116 Object processor = info.getProcessor(); 117 if (processor instanceof WidgetFactory) { 118 WidgetFactory widgetFactoryNodeProcessor = (WidgetFactory) processor; 119 if (sourceURI != null) { 120 widgetFactoryNodeProcessor.resolve(sourceURI, progressMonitor); 121 } 122 123 sourceProcessorRecords.add(new SourceProcessorRecord(source, sourceURI, widgetFactoryNodeProcessor)); 124 } 125 } 126 } 127 } 128 } 129 130 Map<EObject, Collection<Label>> ret = new LinkedHashMap<>(); 131 132 for (SourceProcessorRecord sourceProcessorRecord: sourceProcessorRecords) { 133 Collection<Label> labels = sourceProcessorRecord 134 .widgetFactory() 135 .createLabelsSupplier() 136 .call(progressMonitor, diagnosticConsumer); 137 138 for (Label label: labels) { 139 if (label instanceof Link) { 140 Link link = (Link) label; 141 String location = link.getLocation(); 142 if (!org.nasdanika.common.Util.isBlank(location)) { 143 URI uri = URI.createURI(location); 144 if (sourceProcessorRecord.uri() != null && !uri.isRelative()) { 145 link.setLocation("${base-uri}" + uri.deresolve(sourceProcessorRecord.uri(), true, true, true).toString()); 146 } 147 } 148 } 149 } 150 151 ret.put(sourceProcessorRecord.source(), labels); 152 } 153 154 return ret; 155 } 156 157 protected EObjectReflectiveProcessorFactoryProvider createReflectiveFactoryProvider(Object reflectiveFactory) { 158 return new EObjectReflectiveProcessorFactoryProvider(reflectiveFactory); 159 } 160 161 protected EObjectNodeProcessorReflectiveFactory<Object, Object> createReflectiveFactory() { 162 return new EObjectNodeProcessorReflectiveFactory<>(nodeProcessorFactory); 163 } 164 165 protected Object createConfigFactory() { 166 return new NopEndpointProcessorConfigFactory<>() { 167 168 @Override 169 protected boolean isPassThrough(Connection connection) { 170 return false; 171 } 172 173 }; 174 } 175 176 protected Object createGraphFactory() { 177 return new EObjectGraphFactory(); 178 } 179 180 /** 181 * Calls generateActionModel() and saves the returned label map to a resource at the provided URI 182 * @param sources 183 * @param nodeProcessorFactory 184 * @param references 185 * @param uriResolver 186 * @param diagnosticConsumer 187 * @param actionModelResourceURI Resource URI 188 * @param progressMonitor 189 * @throws IOException 190 */ 191 public void generateActionModel( 192 Consumer<Diagnostic> diagnosticConsumer, 193 URI actionModelResourceURI, 194 ProgressMonitor progressMonitor) throws IOException { 195 196 Map<EObject, Collection<Label>> labelMap = generateActionModel( 197 diagnosticConsumer, 198 progressMonitor); 199 200 saveLabelMap(labelMap, actionModelResourceURI); 201 } 202 203 public static void saveLabelMap(Map<EObject, Collection<Label>> labelMap, URI actionModelResoureURI) throws IOException { 204 ResourceSet actionModelsResourceSet = new ResourceSetImpl(); 205 actionModelsResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl()); 206 207 Resource actionModelResource = actionModelsResourceSet.createResource(actionModelResoureURI); 208 labelMap 209 .values() 210 .stream() 211 .flatMap(Collection::stream) 212 .forEach(actionModelResource.getContents()::add); 213 214 actionModelResource.save(null); 215 } 216 217 /** 218 * Saves generated actions to a file 219 * @param sources 220 * @param nodeProcessorFactory 221 * @param references 222 * @param uriResolver 223 * @param diagnosticConsumer 224 * @param actionModelFile Resource file 225 * @param progressMonitor 226 * @throws IOException 227 */ 228 public void generateActionModel( 229 Consumer<Diagnostic> diagnosticConsumer, 230 File actionModelFile, 231 ProgressMonitor progressMonitor) throws IOException { 232 233 generateActionModel( 234 diagnosticConsumer, 235 URI.createFileURI(actionModelFile.getCanonicalFile().getAbsolutePath()), progressMonitor); 236 } 237 238}