001/* 002 * Copyright 2023 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.search.cocoon; 017 018import java.util.List; 019import java.util.Optional; 020 021import org.apache.commons.lang3.StringUtils; 022import org.apache.commons.lang3.Strings; 023 024import org.ametys.runtime.model.ModelItem; 025import org.ametys.runtime.model.ModelViewItem; 026import org.ametys.runtime.model.SimpleViewItemGroup; 027import org.ametys.runtime.model.View; 028import org.ametys.runtime.model.ViewHelper; 029import org.ametys.runtime.model.ViewItem; 030import org.ametys.runtime.model.ViewItemAccessor; 031 032/** 033 * Helper for search generator 034 */ 035public final class SearchGeneratorHelper 036{ 037 private SearchGeneratorHelper() 038 { 039 // Empty constructor 040 } 041 042 /** 043 * Copy the items of the given accessor, keeping only the items with the given paths 044 * @param viewItemAccessor the view item accessor to copy 045 * @param itemPathsToKeep the paths of the items to keep. If the list is empty, copy the accessor and keep all items 046 * @return the view items accessor's copy 047 */ 048 public static ViewItemAccessor copyAndFilterViewItemAccessor(ViewItemAccessor viewItemAccessor, List<String> itemPathsToKeep) 049 { 050 // Copy the given container, but not its children 051 ViewItemAccessor copy; 052 if (viewItemAccessor instanceof ViewItem viewItem) 053 { 054 copy = (ViewItemAccessor) viewItem.createInstance(); 055 viewItem.copyTo((ViewItem) copy); 056 } 057 else 058 { 059 copy = new View(); 060 ((View) viewItemAccessor).copyTo((View) copy); 061 } 062 063 for (ViewItem viewItem : viewItemAccessor.getViewItems()) 064 { 065 Optional<ViewItem> childCopy = Optional.empty(); 066 if (viewItem instanceof ModelViewItem modelViewItem) 067 { 068 if (_shouldKeepModelViewItem(modelViewItem, itemPathsToKeep)) 069 { 070 if (viewItem instanceof ViewItemAccessor childAccessor) 071 { 072 childCopy = Optional.of((ViewItem) copyAndFilterViewItemAccessor(childAccessor, itemPathsToKeep)); 073 } 074 else 075 { 076 childCopy = Optional.of(viewItem.createInstance()); 077 viewItem.copyTo(childCopy.get()); 078 } 079 } 080 } 081 else if (viewItem instanceof SimpleViewItemGroup group) 082 { 083 childCopy = Optional.of((ViewItem) copyAndFilterViewItemAccessor(group, itemPathsToKeep)); 084 } 085 086 childCopy.ifPresent(copy::addViewItem); 087 } 088 089 return copy; 090 } 091 092 private static boolean _shouldKeepModelViewItem(ModelViewItem modelViewItem, List<String> itemsToKeep) 093 { 094 if (itemsToKeep.isEmpty()) 095 { 096 // If no items are specified, keep them all 097 return true; 098 } 099 100 String modelViewItemPath = ViewHelper.getModelViewItemPath(modelViewItem); 101 for (String itemToKeep : itemsToKeep) 102 { 103 if (Strings.CS.startsWith(itemToKeep, modelViewItemPath)) 104 { 105 if (modelViewItem instanceof ViewItemAccessor viewItemAccessor) 106 { 107 String subItemPath = StringUtils.substring(itemToKeep, modelViewItemPath.length() + 1); 108 109 if (subItemPath.isEmpty() && viewItemAccessor.getViewItems().isEmpty()) 110 { 111 return true; 112 } 113 else if (ViewHelper.hasModelViewItem(viewItemAccessor, subItemPath)) 114 { 115 ModelViewItem subItem = ViewHelper.getModelViewItem(viewItemAccessor, subItemPath); 116 if (subItem instanceof ViewItemAccessor subViewItemAccessor && subViewItemAccessor.getViewItems().isEmpty() 117 || !(subItem instanceof ViewItemAccessor)) 118 { 119 return true; 120 } 121 } 122 } 123 else 124 { 125 return true; 126 } 127 } 128 } 129 130 return false; 131 } 132 133 /** 134 * Removes the items with the given paths from the given accessor 135 * @param viewItemAccessor the view item accessor 136 * @param itemPathsToRemove the paths of the items to remove 137 */ 138 public static void removeResultItems(ViewItemAccessor viewItemAccessor, List<String> itemPathsToRemove) 139 { 140 for (String itemPathToRemove : itemPathsToRemove) 141 { 142 Optional<ModelViewItem> modelViewItem = _getModelViewItem(viewItemAccessor, itemPathToRemove); 143 if (modelViewItem.isPresent()) 144 { 145 ViewItem itemToRemove = modelViewItem.get(); 146 ViewItemAccessor parent = itemToRemove.getParent(); 147 while (parent != null) 148 { 149 parent.removeViewItem(itemToRemove); 150 151 if (parent instanceof ViewItem parentViewItem && parent.getViewItems().isEmpty()) 152 { 153 parent = parentViewItem.getParent(); 154 itemToRemove = parentViewItem; 155 } 156 else 157 { 158 parent = null; 159 } 160 } 161 } 162 } 163 } 164 165 private static Optional<ModelViewItem> _getModelViewItem(ViewItemAccessor viewItemAccessor, String itemPath) throws IllegalArgumentException 166 { 167 String[] pathSegments = StringUtils.split(itemPath, ModelItem.ITEM_PATH_SEPARATOR); 168 169 if (pathSegments == null || pathSegments.length < 1) 170 { 171 throw new IllegalArgumentException("Unable to retrieve the data at the given path. This path is empty."); 172 } 173 else if (pathSegments.length == 1) 174 { 175 for (ViewItem viewItem : viewItemAccessor.getViewItems()) 176 { 177 if (viewItem instanceof ModelViewItem modelViewItem 178 && _isSearchedModelViewItem(modelViewItem, itemPath)) 179 { 180 return Optional.of(modelViewItem); 181 } 182 else if (viewItem instanceof SimpleViewItemGroup group) 183 { 184 Optional<ModelViewItem> modelViewItem = _getModelViewItem(group, itemPath); 185 if (modelViewItem.isPresent()) 186 { 187 return modelViewItem; 188 } 189 } 190 } 191 } 192 else 193 { 194 String fisrtSegment = pathSegments[0]; 195 String subPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, pathSegments.length); 196 for (ViewItem viewItem : viewItemAccessor.getViewItems()) 197 { 198 if (viewItem instanceof ViewItemAccessor childAccessor) 199 { 200 Optional<ModelViewItem> modelViewItem = Optional.empty(); 201 if (viewItem instanceof SimpleViewItemGroup) 202 { 203 modelViewItem = _getModelViewItem(childAccessor, itemPath); 204 } 205 else if (viewItem.getName().equals(fisrtSegment)) 206 { 207 modelViewItem = _getModelViewItem(childAccessor, subPath); 208 } 209 210 if (modelViewItem.isPresent()) 211 { 212 return modelViewItem; 213 } 214 } 215 } 216 } 217 218 // no model view item has been found with this path 219 return Optional.empty(); 220 } 221 222 private static boolean _isSearchedModelViewItem(ModelViewItem modelViewItem, String itemName) 223 { 224 return modelViewItem.getName().equals(itemName) 225 && (!(modelViewItem instanceof ViewItemAccessor) 226 || modelViewItem instanceof ViewItemAccessor childAccessor && childAccessor.getViewItems().isEmpty()); 227 } 228}