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