001/* 002 * Copyright 2015 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.model.properties; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.solr.common.SolrInputDocument; 029import org.xml.sax.ContentHandler; 030import org.xml.sax.SAXException; 031 032import org.ametys.cms.contenttype.RichTextAttributeDefinition; 033import org.ametys.cms.data.RichText; 034import org.ametys.cms.data.type.indexing.IndexableDataContext; 035import org.ametys.cms.repository.Content; 036import org.ametys.cms.search.SearchField; 037import org.ametys.cms.search.query.Query; 038import org.ametys.cms.search.query.Query.Operator; 039import org.ametys.cms.search.systemprop.AbstractSystemProperty; 040import org.ametys.core.util.JSONUtils; 041import org.ametys.runtime.model.Model; 042import org.ametys.runtime.model.ModelHelper; 043import org.ametys.runtime.model.type.DataContext; 044import org.ametys.runtime.model.type.ModelItemTypeConstants; 045 046/** 047 * Property for semantic annotations. 048 * Do not use the property in search model. There is no search field nor query, the criteria would not work 049 */ 050public class SemanticAnnotationSystemProperty extends AbstractSystemProperty<String, Content> 051{ 052 /** Prefix for annotations */ 053 public static final String ANNOTATION_PREFIX = "annotation-"; 054 055 /** The JSON utils */ 056 protected JSONUtils _jsonUtils; 057 058 @Override 059 public void service(ServiceManager manager) throws ServiceException 060 { 061 super.service(manager); 062 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 063 } 064 065 @Override 066 public boolean isMultiple() 067 { 068 return true; 069 } 070 071 @Override 072 public boolean isSortable() 073 { 074 return false; 075 } 076 077 @Override 078 public boolean isDisplayable() 079 { 080 return false; 081 } 082 083 @Override 084 public Object getValue(Content content) 085 { 086 List<String> values = new ArrayList<>(); 087 088 Map<String, List<String>> semanticAnnotations = _getSemanticAnnotations(content); 089 for (String annotationName : semanticAnnotations.keySet()) 090 { 091 String value = annotationName + ":" + _jsonUtils.convertObjectToJson(semanticAnnotations.get(annotationName)); 092 values.add(value); 093 } 094 095 return values.toArray(new String[values.size()]); 096 } 097 098 public Object valueToJSON(Content content, DataContext context) 099 { 100 return _getSemanticAnnotations(content); 101 } 102 103 public void valueToSAX(ContentHandler contentHandler, Content content, DataContext context) throws SAXException 104 { 105 Map<String, List<String>> semanticAnnotations = _getSemanticAnnotations(content); 106 for (String annotationName : semanticAnnotations.keySet()) 107 { 108 List<String> semanticAnnotation = semanticAnnotations.get(annotationName); 109 String[] semanticAnnotationAsStringArray = semanticAnnotation.toArray(new String[semanticAnnotation.size()]); 110 getType().valueToSAX(contentHandler, annotationName, semanticAnnotationAsStringArray, context); 111 } 112 } 113 114 @Override 115 public void indexValue(SolrInputDocument document, Content content, IndexableDataContext context) 116 { 117 Map<String, List<String>> semanticAnnotations = _getSemanticAnnotations(content); 118 for (String annotationName : semanticAnnotations.keySet()) 119 { 120 List<String> semanticAnnotation = semanticAnnotations.get(annotationName); 121 String[] semanticAnnotationAsStringArray = semanticAnnotation.toArray(new String[semanticAnnotation.size()]); 122 getType().indexValue(document, document, annotationName, semanticAnnotationAsStringArray, context); 123 } 124 } 125 126 private Map<String, List<String>> _getSemanticAnnotations(Content content) 127 { 128 // Search for all rich text definitions 129 Set<RichTextAttributeDefinition> richTextDefinitions = new HashSet<>(); 130 for (Model model : content.getModel()) 131 { 132 ModelHelper.findModelItemsByType(model, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID) 133 .stream() 134 .filter(RichTextAttributeDefinition.class::isInstance) 135 .map(RichTextAttributeDefinition.class::cast) 136 .forEach(richTextDefinitions::add); 137 } 138 139 // For each richText definition, get the rich text values 140 Map<String, List<String>> semanticAnnotations = new HashMap<>(); 141 for (RichTextAttributeDefinition richTextDefinition : richTextDefinitions) 142 { 143 Object value = content.getValue(richTextDefinition.getPath(), true); 144 List<RichText> richTexts = null; 145 if (value instanceof RichText richText) 146 { 147 richTexts = List.of(richText); 148 } 149 else if (value instanceof RichText[] richTextsArray) 150 { 151 richTexts = Arrays.asList(richTextsArray); 152 } 153 154 if (richTexts != null) 155 { 156 for (RichText richText : richTexts) 157 { 158 Map<String, List<String>> richTextAnnotations = richText.getAllAnnotations(); 159 for (String richTextAnnotationName : richTextAnnotations.keySet()) 160 { 161 String prefixedAnnotationName = ANNOTATION_PREFIX + richTextAnnotationName; 162 List<String> semanticAnnotationValues = semanticAnnotations.computeIfAbsent(prefixedAnnotationName, __ -> new ArrayList<>()); 163 semanticAnnotationValues.addAll(richTextAnnotations.get(richTextAnnotationName)); 164 } 165 } 166 } 167 } 168 169 return semanticAnnotations; 170 } 171 172 @Override 173 protected String _getTypeId() 174 { 175 return ModelItemTypeConstants.STRING_TYPE_ID; 176 } 177 178 public Query getQuery(Object value, Operator operator, String language, Map<String, Object> contextualParameters) 179 { 180 return null; 181 } 182 183 public SearchField getSearchField() 184 { 185 return null; 186 } 187}