1 /*
2  * Copyright (c) 2008-2019 Emmanuel Dupuy.
3  * This project is distributed under the GPLv3 license.
4  * This is a Copyleft license that gives the user the right to use,
5  * copy and modify the code freely for non-commercial purposes.
6  */
7 
8 package org.jd.gui.controller;
9 
10 import org.jd.gui.api.API;
11 import org.jd.gui.api.model.Container;
12 import org.jd.gui.api.model.Type;
13 import org.jd.gui.model.container.DelegatingFilterContainer;
14 import org.jd.gui.service.type.TypeFactoryService;
15 import org.jd.gui.spi.TypeFactory;
16 import org.jd.gui.view.SelectLocationView;
17 
18 import javax.swing.*;
19 import java.awt.*;
20 import java.net.URI;
21 import java.util.*;
22 import java.util.function.Consumer;
23 
24 public class SelectLocationController {
25     protected static final ContainerEntryComparator CONTAINER_ENTRY_COMPARATOR = new ContainerEntryComparator();
26 
27     protected API api;
28     protected SelectLocationView selectLocationView;
29 
SelectLocationController(API api, JFrame mainFrame)30     public SelectLocationController(API api, JFrame mainFrame) {
31         this.api = api;
32         // Create UI
33         selectLocationView = new SelectLocationView(api, mainFrame);
34     }
35 
36     @SuppressWarnings("unchecked")
show(Point location, Collection<Container.Entry> entries, Consumer<Container.Entry> selectedLocationCallback, Runnable closeCallback)37     public void show(Point location, Collection<Container.Entry> entries, Consumer<Container.Entry> selectedLocationCallback, Runnable closeCallback) {
38         // Show UI
39         HashMap<Container, ArrayList<Container.Entry>> map = new HashMap<>();
40 
41         for (Container.Entry entry : entries) {
42             Container container = entry.getContainer();
43 
44             // Search root container
45             while (true) {
46                 Container parentContainer = container.getRoot().getParent().getContainer();
47                 if (parentContainer.getRoot() == null) {
48                     break;
49                 } else {
50                     container = parentContainer;
51                 }
52             }
53 
54             ArrayList<Container.Entry> list = map.get(container);
55 
56             if (list == null) {
57                 map.put(container, list=new ArrayList<>());
58             }
59 
60             list.add(entry);
61         }
62 
63         HashSet<DelegatingFilterContainer> delegatingFilterContainers = new HashSet<>();
64 
65         for (Map.Entry<Container, ArrayList<Container.Entry>> mapEntry : map.entrySet()) {
66             Container container = mapEntry.getKey();
67             // Create a filtered container
68             // TODO In a future release, display matching types and inner-types, not only matching files
69             delegatingFilterContainers.add(new DelegatingFilterContainer(container, getOuterEntries(mapEntry.getValue())));
70         }
71 
72         Consumer<URI> selectedEntryCallback = uri -> onLocationSelected(delegatingFilterContainers, uri, selectedLocationCallback);
73 
74         selectLocationView.show(location, delegatingFilterContainers, entries.size(), selectedEntryCallback, closeCallback);
75     }
76 
getOuterEntries(Collection<Container.Entry> entries)77     protected Collection<Container.Entry> getOuterEntries(Collection<Container.Entry> entries) {
78         HashMap<Container.Entry, Container.Entry> innerTypeEntryToOuterTypeEntry = new HashMap<>();
79         HashSet<Container.Entry> outerEntriesSet = new HashSet<>();
80 
81         for (Container.Entry entry : entries) {
82             Container.Entry outerTypeEntry = null;
83             TypeFactory factory = TypeFactoryService.getInstance().get(entry);
84 
85             if (factory != null) {
86                 Type type = factory.make(api, entry, null);
87 
88                 if ((type != null) && (type.getOuterName() != null)) {
89                     outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);
90 
91                     if (outerTypeEntry == null) {
92                         HashMap<String, Container.Entry> typeNameToEntry = new HashMap<>();
93                         HashMap<String, String> innerTypeNameToOuterTypeName = new HashMap<>();
94 
95                         // Populate "typeNameToEntry" and "innerTypeNameToOuterTypeName"
96                         for (Container.Entry e : entry.getParent().getChildren()) {
97                             factory = TypeFactoryService.getInstance().get(e);
98 
99                             if (factory != null) {
100                                 type = factory.make(api, e, null);
101 
102                                 if (type != null) {
103                                     typeNameToEntry.put(type.getName(), e);
104                                     if (type.getOuterName() != null) {
105                                         innerTypeNameToOuterTypeName.put(type.getName(), type.getOuterName());
106                                     }
107                                 }
108                             }
109                         }
110 
111                         // Search outer type entries and populate "innerTypeEntryToOuterTypeEntry"
112                         for (Map.Entry<String, String> e : innerTypeNameToOuterTypeName.entrySet()) {
113                             Container.Entry innerTypeEntry = typeNameToEntry.get(e.getKey());
114 
115                             if (innerTypeEntry != null) {
116                                 String outerTypeName = e.getValue();
117 
118                                 for (;;) {
119                                     String typeName = innerTypeNameToOuterTypeName.get(outerTypeName);
120                                     if (typeName != null) {
121                                         outerTypeName = typeName;
122                                     } else {
123                                         break;
124                                     }
125                                 }
126 
127                                 outerTypeEntry = typeNameToEntry.get(outerTypeName);
128 
129                                 if (outerTypeEntry != null) {
130                                     innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry);
131                                 }
132                             }
133                         }
134 
135                         // Get outer type entry
136                         outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);
137                     }
138                 }
139             }
140 
141             if (outerTypeEntry != null) {
142                 outerEntriesSet.add(outerTypeEntry);
143             } else {
144                 outerEntriesSet.add(entry);
145             }
146         }
147 
148         // Return outer type entries sorted by path
149         ArrayList<Container.Entry> result = new ArrayList<>(outerEntriesSet);
150 
151         result.sort(CONTAINER_ENTRY_COMPARATOR);
152 
153         return result;
154     }
155 
onLocationSelected(Set<DelegatingFilterContainer> delegatingFilterContainers, URI uri, Consumer<Container.Entry> selectedLocationCallback)156     protected void onLocationSelected(Set<DelegatingFilterContainer> delegatingFilterContainers, URI uri, Consumer<Container.Entry> selectedLocationCallback) {
157         // Open the single entry uri
158         Container.Entry entry = null;
159 
160         for (DelegatingFilterContainer container : delegatingFilterContainers) {
161             entry = container.getEntry(uri);
162             if (entry != null) {
163                 break;
164             }
165         }
166 
167         if (entry != null) {
168             selectedLocationCallback.accept(entry);
169         }
170     }
171 
172     protected static class ContainerEntryComparator implements Comparator<Container.Entry> {
173         @Override
compare(Container.Entry e1, Container.Entry e2)174         public int compare(Container.Entry e1, Container.Entry e2) {
175             return e1.getPath().compareTo(e2.getPath());
176         }
177     }
178 }
179