1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.resources.app.data;
35
36 import static info.magnolia.resources.app.workbench.tree.ResourcesTreePresenter.*;
37
38 import info.magnolia.jcr.RuntimeRepositoryException;
39 import info.magnolia.jcr.util.NodeTypes;
40 import info.magnolia.module.ModuleRegistry;
41 import info.magnolia.resourceloader.Resource;
42 import info.magnolia.resourceloader.ResourceOrigin;
43 import info.magnolia.resourceloader.classpath.ClasspathResource;
44 import info.magnolia.resourceloader.layered.LayeredResource;
45 import info.magnolia.ui.filter.DataFilter;
46 import info.magnolia.ui.datasource.optionlist.Option;
47
48 import java.util.Arrays;
49 import java.util.List;
50 import java.util.stream.Stream;
51
52 import javax.inject.Inject;
53 import javax.jcr.Node;
54 import javax.jcr.RepositoryException;
55
56 import org.apache.commons.lang3.StringUtils;
57
58 import com.vaadin.data.provider.AbstractBackEndHierarchicalDataProvider;
59 import com.vaadin.data.provider.HierarchicalQuery;
60
61
62
63
64 public class ResourceDataProvider extends AbstractBackEndHierarchicalDataProvider<Resource, DataFilter> implements ResourceHelper {
65
66 private final ResourceOrigin resourceOrigin;
67 private final ModuleRegistry moduleRegistry;
68
69 @Inject
70 public ResourceDataProvider(ResourceOrigin resourceOrigin, ModuleRegistry moduleRegistry) {
71 this.resourceOrigin = resourceOrigin;
72 this.moduleRegistry = moduleRegistry;
73 }
74
75 @Override
76 public Object getId(Resource item) {
77 return item.getPath();
78 }
79
80 @Override
81 protected Stream<Resource> fetchChildrenFromBackEnd(HierarchicalQuery<Resource, DataFilter> query) {
82 boolean isRoot = query.getParent() == null;
83 Stream<Resource> resourceStream = !isRoot ?
84 getChildren(query.getParent()) :
85 getChildren(resourceOrigin.getRoot())
86 .filter(resource -> {
87
88 boolean originFilterPresent = query.getFilter()
89 .map(DataFilter::getPropertyFilters)
90 .map(stringObjectMap -> stringObjectMap.get("origin") != null)
91 .orElse(false);
92
93 boolean isClasspathOnlyResource = getFirstResource(resource)
94 .map(ClasspathResource.class::isInstance)
95 .orElse(false);
96
97 return originFilterPresent || !isClasspathOnlyResource || moduleRegistry.getModuleNames().contains(resource.getName());
98 });
99
100 return query.getFilter().map(filter ->
101 resourceStream
102 .filter(resource -> applyFilter(resource, filter))
103 .filter(resource -> !resource.isDirectory() || hasNonFilteredChildren(resource, filter)))
104 .orElse(resourceStream);
105 }
106
107 private boolean hasNonFilteredChildren(Resource resource, DataFilter query) {
108 return getChildCount(new HierarchicalQuery<>(query, resource)) != 0;
109 }
110
111 private boolean applyFilter(Resource resource, DataFilter filter) {
112 return filter.getPropertyFilters().entrySet().stream()
113 .filter(stringObjectEntry -> !"".equals(stringObjectEntry.getValue()))
114 .allMatch(stringObjectEntry -> filter(stringObjectEntry.getValue(), resource, stringObjectEntry.getKey()));
115 }
116
117 @Override
118 public int getChildCount(HierarchicalQuery<Resource, DataFilter> query) {
119 return (int) fetchChildrenFromBackEnd(query).count();
120 }
121
122 @Override
123 public boolean hasChildren(Resource item) {
124 return getChildren(item).findFirst().isPresent();
125 }
126
127 private Stream<Resource> getChildren(Resource parent) {
128 return !parent.isDirectory() ? Stream.empty() : parent.listChildren().stream();
129 }
130
131 private boolean filter(Object filterValue, Resource resource, String propertyName) {
132 if (filterValue == null) {
133 return true;
134 } else if (COLUMN_ORIGIN.equals(propertyName)){
135 return filterByOrigin((ResourceOrigin) filterValue, resource);
136 } else if (COLUMN_TYPE.equals(propertyName)) {
137 return resource.isDirectory() || StringUtils.contains(TIKA.detect(resource.getPath()), (String) filterValue);
138 } else if (COLUMN_NAME.equals(propertyName)) {
139 return resource.isDirectory() || resource.getName().contains((String) filterValue);
140 } else if (COLUMN_OVERRIDDEN.equals(propertyName)) {
141 return !((Boolean) filterValue) || (resource instanceof LayeredResource && ((LayeredResource) resource).getLayers().size() > 1);
142 } else if (COLUMN_STATUS.equals(propertyName)) {
143 int expectedStatus = Integer.parseInt(((Option) filterValue).getValue());
144 return getJcrNode(resource)
145 .map(this::getActivationStatus)
146 .map(integer -> integer == expectedStatus)
147 .orElse(false);
148 }
149 throw new IllegalArgumentException("Unsupported filter property: " + propertyName);
150 }
151
152 private boolean filterByOrigin(ResourceOrigin filterValue, Resource resource) {
153 List<Resource> resources = resource instanceof LayeredResource ? ((LayeredResource) resource).getLayers() : Arrays.asList(resource);
154 return resources.stream()
155 .map(Resource::getOrigin)
156 .anyMatch(originStream -> originStream.getClass().equals(filterValue.getClass()));
157 }
158
159 private int getActivationStatus(Node node) {
160 try {
161 return NodeTypes.Activatable.getActivationStatus(node);
162 } catch (RepositoryException e) {
163 throw new RuntimeRepositoryException(e);
164 }
165 }
166 }
167