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