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}