Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2014 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.cq.social.srp.internal;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.cq.social.srp.utilities.internal.InternalSocialResourceUtilities;
import com.adobe.granite.activitystreams.JsonConstants;
/**
* This class maps from SoCo keys and values to those used by the common schema developed by Adobe Cloud Storage. This
* class is only intended to be used by Communities code.
*/
public abstract class AbstractSchemaMapper {
/* ---------------- Stored Document fields ---------------- */
/** the different descriptions that can be possible. */
public static final String AS_ACCEPTFILETYPES = "acceptFileTypes";
public static final String AS_ALLOWFILEUPLOADS = "allowFileUploads";
public static final String AS_ALLOW_REPLIES = "allowRepliesToComments";
public static final String AS_APPROVED = "approved_b";
public static final String AS_AUTHOR = "author_username";
public static final String AS_AUTHORIZABLE_ID = "authorizableId_t";
public static final String AS_AUTHOR_DISPLAY_NAME = "author_display_name";
public static final String AS_AUTHOR_DISPLAY_NAME_CI = "author_display_name_ci";
public static final String AS_AUTHOR_PROFILE_URL = "author_profile_url";
public static final String AS_AUTHOR_CI = "author_username_ci";
public static final String AS_AUTHOR_IMAGE_URL = "author_image_url";
public static final String AS_BASE_TYPE = "base_type_s";
public static final String AS_IS_CLOSED = "is_closed_b";
public static final String AS_COMPANY_ID = "company_db_id";
public static final String AS_COMPOSED_BY = "composedBy_t";
public static final String AS_CQ_DATA = "cqdata";
public static final String AS_CQTAGS = "cqtags_ss";
public static final String AS_CREATED = "timestamp";
public static final String AS_ENTITY_URL = "entity_url";
public static final String AS_FEATURES = "features";
public static final String AS_ID = "_id"; // OPADS used to have this as id
public static final String AS_IS_DRAFT = "is_draft_b";
public static final String AS_IS_FLAGGED = "is_flagged_b";
public static final String AS_IS_FLAGGED_HIDDEN = "is_flaggedHidden_b";
public static final String AS_IS_REPLY = "is_reply_b";
public static final String AS_MAXFILESIZE = "maxFileSize";
public static final String AS_MAXIMAGEFILESIZE = "maxImageFileSize";
public static final String AS_MODERATE_COMMENTS = "moderateComments";
public static final String AS_PARENT_ID = "parent_id_s";
public static final String AS_PINNED = "pinned_b";
public static final String AS_PROVIDER_ID = "provider_id";
public static final String AS_PUBLISH_DATE = "publishDate_dt";
public static final String AS_PUBLISH_JOB_ID = "publishJobId_s";
public static final String AS_REPORT_SUITE = "report_suite";
public static final String AS_REQUIRELOGIN = "requireLogin";
public static final String AS_RESOURCE_TYPE = "resource_type_s";
public static final String AS_ROOT_COMMENT_SYSTEM = "thread_id_s"; // OPADS used to have rootcommentsystem_s
public static final String AS_RTEENABLED = "rteEnabled";
public static final String AS_TALLY_RESPONSE = "response_s";
public static final String AS_TITLE = "title_t";
public static final String AS_VERBATIM = "verbatim";
public static final String AS_VERBATIM_LANGUAGE_INDEX = "verbatim_";
public static final String AS_ATTACHMENT_LENGTH = "length";
public static final String AS_UPLOADDATE = "uploadDate";
public static final String AS_ADDRESS = "address_s";
// Unique solr 'id' required to be present
public static final String SOLR_ID = "id";
/*
* Even though the sentiment field name is the same in AS and SoCo, we have to define it so that it doesn't go in
* cq_data
*/
public static final String AS_SENTIMENT = "sentiment";
/* ---------------- Stored attachment fields ---------------- */
public static final String AS_CONTENT_TYPE = "content-type";
public static final String AS_ATTACHMENT = "attachment";
/* ---------------- Activity Streams searchable fields ---------------- */
public static final String AS_VERB = "verb_s";
public static final String AS_ACTOR_ID = "actorid_s";
/* ---------------- SoCo doc fields ---------------- */
private static final String CQ_ACCEPTFILETYPES = "acceptFileTypes";
public static final String CQ_ADDED = InternalSocialResourceUtilities.PN_DATE;
public static final String CQ_ALLOWFILEUPLOADS = "allowFileUploads";
public static final String CQ_ALLOW_REPLIES = "allowRepliesToComments";
public static final String CQ_APPROVED = "approved";
public static final String CQ_AUTHORIZABLE_ID = "authorizableId";
public static final String CQ_AUTHOR_IMAGE_URL = "author_image_url";
public static final String CQ_AUTHOR_PROFILE_URL = "author_profile_url";
public static final String CQ_AUTHOR_DISPLAY_NAME = "author_display_name";
public static final String CQ_AUTHOR_EMAIL = "author_display_name";
public static final String CQ_BASE_TYPE = InternalSocialResourceUtilities.PN_BASETYPE;
public static final String CQ_COMPOSED_BY = "composedBy";
public static final String CQ_DRAFT = "isDraft";
public static final String CQ_IS_CLOSED = "isClosed";
public static final String CQ_ENTITY_URL = "entity_url";
public static final String CQ_IS_FLAGGED = "isFlagged";
public static final String CQ_FLAGGED_HIDDEN = "isFlaggedHidden";
public static final String CQ_IS_REPLY = InternalSocialResourceUtilities.PN_IS_REPLY;
public static final String CQ_KEY = InternalSocialResourceUtilities.PN_DS_KEY;
public static final String CQ_MAXFILESIZE = "maxFileSize";
public static final String CQ_MAXIMAGEFILESIZE = "maxImageFileSize";
public static final String CQ_MODERATE_COMMENTS = "moderateComments";
public static final String CQ_PARENT_ID = InternalSocialResourceUtilities.PN_PARENTID;
public static final String CQ_PINNED = "pinned";
public static final String CQ_PUBLISH_DATE = "publishDate";
public static final String CQ_PUBLISH_JOB_ID = "publishJobId";
public static final String CQ_REQUIRELOGIN = "requireLogin";
public static final String CQ_RESOURCE_TYPE = SlingConstants.NAMESPACE_PREFIX + ":"
+ SlingConstants.PROPERTY_RESOURCE_TYPE;
public static final String CQ_ROOT_COMMENT_SYSTEM = InternalSocialResourceUtilities.PN_CS_ROOT;
public static final String CQ_RTEENABLED = "rteEnabled";
public static final String CQ_SPAM = "isSpam";
public static final String CQ_TAGS = "cq:tags";
public static final String CQ_TALLY_RESPONSE = "response";
public static final String CQ_TALLY_TIMESTAMP = "timestamp";
public static final String CQ_TITLE = "jcr:title";
public static final String CQ_USER_IDENTIFIER = "userIdentifier";
public static final String CQ_DESCRIPTION = "jcr:description";
public static final String CQ_LAST_MODIFIED = "cq:lastModified";
public static final String CQ_SENTIMENT = "sentiment";
/* ---------------- SoCo attachment fields ---------------- */
public static final String CQ_ATTACHMENT = "nt:file";
public static final String CQ_CONTENT_TYPE = "mimetype";
/**
* Search collections has its own mapping for some fields.
*/
public static final String CQ_SEARCH_PATH = ":path"; // path filter from isearch collections
public static final String CQ_SEARCH_TAG_NAME = "cq:tags"; // tag in lucene query string from search collections
public static final String CQ_SEARCH_PARENT_ID = ":parent"; // path filter from isearch collections
/*
* Machine Translation Fields
*/
public static final String CQ_MT_TRANS_DATE = "translationDate";
public static final String CQ_MT_TRANSLATION = "translation";
protected static final String CQ_MT_LANGAUGE = "mtlanguage";
/*
* Activity Streams fields
*/
public static final String CQ_AS_VERB = JsonConstants.PROPERTY_VERB;
public static final String CQ_AS_ACTOR_ID = JsonConstants.PROPERTY_ACTOR_ID;
/* suffix pattern to identify searchable keys, as defined in the solr schema_soco.xml config file */
private static final String SEARCHABLE_SUFFIX =
"s|ss|t|ts|b|bs|i|is|tl|tls|f|fs|d|ds|dt|dts|tf|tfs|td|tds|tdt|tdts|latlong|latlongs|tg|tgs|en|ens|ja|jas";
/*
* This is the regular exp. that determines if a key (name) is searchable or not. [^\\s] - must start with one or
* more characters except white space \\_ - follow by a underscore '_' ?i - ignore case in match $ - end of the
* string
*/
private static final String SUFFIX_PATTERN = "([^\\s]+(\\_(?i)(" + SEARCHABLE_SUFFIX + "))$)";
private static final Pattern PATTERN = Pattern.compile(SUFFIX_PATTERN);
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String CQ_DATE_FORMAT = "EEE MMM dd HH:mm:ss zzz yyyy";
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSchemaMapper.class);
// These are the sets of attachment related strings that need explicit mapping. Everything else is blindly
// copied to the cqdata subhash (if not a searchable key) or to the toplevel hash (if a searchable key)..
private final Set CQ_ATTACHMENT_KEYS;
private final Set AS_ATTACHMENT_KEYS;
private final Map TOP_LEVEL_TO_SCHEMA_KEYS;
private final Map TOP_LEVEL_TO_CQ_KEYS;
private final Map TRANSLATION_TO_SCHEMA_KEYS;
// Don't want to maintain it in two places. Using dates would be nice, but AS JSOn would be a pain.
private static final List SUBHASH_DATE_FIELDS = Arrays.asList(CQ_LAST_MODIFIED, CQ_TALLY_TIMESTAMP,
CQ_MT_TRANS_DATE, "jcr:created", "jcr:lastModified");
/**
* Prefix for non dynamic dates so we can convert from the long format back to calendar.
*/
public static final String AS_CQ_SUBHASH_DATE = "SRP_CQ_SUBHASH_123098_DATE_";
/** ctor. */
public AbstractSchemaMapper() {
TOP_LEVEL_TO_CQ_KEYS = new HashMap();
TOP_LEVEL_TO_CQ_KEYS.put(AS_APPROVED, CQ_APPROVED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_CQTAGS, CQ_TAGS);
TOP_LEVEL_TO_CQ_KEYS.put(AS_CREATED, CQ_ADDED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_IS_DRAFT, CQ_DRAFT);
TOP_LEVEL_TO_CQ_KEYS.put(AS_PINNED, CQ_PINNED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_RESOURCE_TYPE, CQ_RESOURCE_TYPE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_TITLE, CQ_TITLE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_PARENT_ID, CQ_PARENT_ID);
TOP_LEVEL_TO_CQ_KEYS.put(AS_PROVIDER_ID, CQ_KEY);
TOP_LEVEL_TO_CQ_KEYS.put(AS_AUTHOR, CQ_USER_IDENTIFIER);
TOP_LEVEL_TO_CQ_KEYS.put(AS_AUTHORIZABLE_ID, CQ_AUTHORIZABLE_ID);
TOP_LEVEL_TO_CQ_KEYS.put(AS_COMPOSED_BY, CQ_COMPOSED_BY);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ROOT_COMMENT_SYSTEM, CQ_ROOT_COMMENT_SYSTEM);
TOP_LEVEL_TO_CQ_KEYS.put(AS_TALLY_RESPONSE, CQ_TALLY_RESPONSE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_AUTHOR_IMAGE_URL, CQ_AUTHOR_IMAGE_URL);
TOP_LEVEL_TO_CQ_KEYS.put(AS_AUTHOR_PROFILE_URL, CQ_AUTHOR_PROFILE_URL);
TOP_LEVEL_TO_CQ_KEYS.put(AS_AUTHOR_DISPLAY_NAME, CQ_AUTHOR_DISPLAY_NAME);
TOP_LEVEL_TO_CQ_KEYS.put(AS_BASE_TYPE, CQ_BASE_TYPE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ENTITY_URL, CQ_ENTITY_URL);
TOP_LEVEL_TO_CQ_KEYS.put(AS_IS_REPLY, CQ_IS_REPLY);
TOP_LEVEL_TO_CQ_KEYS.put(AS_MODERATE_COMMENTS, CQ_MODERATE_COMMENTS);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ALLOW_REPLIES, CQ_ALLOW_REPLIES);
TOP_LEVEL_TO_CQ_KEYS.put(AS_IS_CLOSED, CQ_IS_CLOSED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_IS_FLAGGED, CQ_IS_FLAGGED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_IS_FLAGGED_HIDDEN, CQ_FLAGGED_HIDDEN);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ALLOWFILEUPLOADS, CQ_ALLOWFILEUPLOADS);
TOP_LEVEL_TO_CQ_KEYS.put(AS_MAXFILESIZE, CQ_MAXFILESIZE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ACCEPTFILETYPES, CQ_ACCEPTFILETYPES);
TOP_LEVEL_TO_CQ_KEYS.put(AS_RTEENABLED, CQ_RTEENABLED);
TOP_LEVEL_TO_CQ_KEYS.put(AS_REQUIRELOGIN, CQ_REQUIRELOGIN);
TOP_LEVEL_TO_CQ_KEYS.put(AS_MAXIMAGEFILESIZE, CQ_MAXIMAGEFILESIZE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_PUBLISH_DATE, CQ_PUBLISH_DATE);
TOP_LEVEL_TO_CQ_KEYS.put(AS_PUBLISH_JOB_ID, CQ_PUBLISH_JOB_ID);
TOP_LEVEL_TO_CQ_KEYS.put(AS_ACTOR_ID, CQ_AS_ACTOR_ID);
TOP_LEVEL_TO_CQ_KEYS.put(AS_VERB, CQ_AS_VERB);
TOP_LEVEL_TO_CQ_KEYS.put(AS_SENTIMENT, CQ_SENTIMENT);
final Map mapperKeys = getMapperSpecificKeys();
TOP_LEVEL_TO_CQ_KEYS.putAll(mapperKeys);
TOP_LEVEL_TO_SCHEMA_KEYS = new HashMap(TOP_LEVEL_TO_CQ_KEYS.size());
for (final Entry entry : TOP_LEVEL_TO_CQ_KEYS.entrySet()) {
TOP_LEVEL_TO_SCHEMA_KEYS.put(entry.getValue(), entry.getKey());
}
AS_ATTACHMENT_KEYS = new HashSet();
AS_ATTACHMENT_KEYS.add(AS_CONTENT_TYPE);
AS_ATTACHMENT_KEYS.add(AS_ATTACHMENT);
AS_ATTACHMENT_KEYS.add(AS_PROVIDER_ID);
AS_ATTACHMENT_KEYS.add(AS_UPLOADDATE);
CQ_ATTACHMENT_KEYS = new HashSet();
CQ_ATTACHMENT_KEYS.add(CQ_CONTENT_TYPE);
CQ_ATTACHMENT_KEYS.add(CQ_ATTACHMENT);
TRANSLATION_TO_SCHEMA_KEYS = new HashMap();
updateTranslationSchemaKeys(TRANSLATION_TO_SCHEMA_KEYS);
}
/**
* Update translation schema keys
* @param translationSchmeKeys Translation Schema Keys
*/
protected void updateTranslationSchemaKeys(final Map translationSchmeKeys) {
// by default this is empty method
}
/**
* fetch the keys that are specific to this impl that need to be mapped.
* @return the keys (going from the schema key to the soco key) to be mapped
*/
public abstract Map getMapperSpecificKeys();
/** @return the report suite used in this mapper. */
public abstract String getReportSuite();
/**
* set the report suite used in this mapper.
* @param reportSuite the report suite
*/
public abstract void setReportSuite(String reportSuite);
/**
* @return the key used in the db schema that tells the description
*/
public static String getSchemaDescriptionKey() {
return AS_VERBATIM;
}
/**
* @return the key used in the schema to tell the resource type.
*/
public static String getSchemaResourceTypeKey() {
return AS_RESOURCE_TYPE;
}
/**
* @return the key used in the schema to tell the location of the root comment system.
*/
public static String getSchemaRootCommentSystemKey() {
return AS_ROOT_COMMENT_SYSTEM;
}
/**
* @return the key used in soco to tell the author's display name.
*/
public static String getSocoAuthorDisplayNameKey() {
return CQ_AUTHOR_DISPLAY_NAME;
}
/**
* @return the key used in the soco to tell the author's avatar url.
*/
public static String getSocoAuthorImageUrlKey() {
return CQ_AUTHOR_IMAGE_URL;
}
/**
* @return the key used in the soco to tell the author's avatar url.
*/
public static String getSocoAuthorProfileUrlKey() {
return CQ_AUTHOR_PROFILE_URL;
}
/**
* @return the key used in the soco to tell the entity url.
*/
public static String getSocoEntityUrlKey() {
return CQ_ENTITY_URL;
}
/**
* @return the key used in the soco to tell approval status.
*/
public static String getSocoApprovedKey() {
return CQ_APPROVED;
}
/**
* @return the key used in the soco to tell approval status.
*/
public static String getSocoFlaggedHiddenKey() {
return CQ_FLAGGED_HIDDEN;
}
public static String getSocoDraftKey() {
return CQ_DRAFT;
}
/**
* @return the key used in the schema to tell the approval status.
*/
public static String getSchemaApprovedKey() {
return AS_APPROVED;
}
/**
* @return the key used in the schema to tell the approval status.
*/
public static String getSchemaFlaggedHiddenKey() {
return AS_IS_FLAGGED_HIDDEN;
}
public static String getSchemaIsDraftKey() {
return AS_IS_DRAFT;
}
/**
* @return the key used in the schema to tell the parent id.
*/
public static String getSchemaParentIdKey() {
return AS_PARENT_ID;
}
/**
* @return the key used in the schema to tell the provider id.
*/
public static String getSchemaProviderIdKey() {
return AS_PROVIDER_ID;
}
/**
* @return the key used in the schema to tell the creation time.
*/
public static String getSchemaTimestampKey() {
return AS_CREATED;
}
/**
* @return the key used in schema to tell the attachment length.
*/
public static String getSchemaAttachmentLengthKey() {
return AS_ATTACHMENT_LENGTH;
}
/**
* @return the key used in schema to tell the attachment creation time.
*/
public static String getSchemaAttachmentUploadDateKey() {
return AS_UPLOADDATE;
}
/**
* @return the key used in soco to tell the creation time.
*/
public static String getSocoAddedKey() {
return CQ_ADDED;
}
/**
* @return the key used in soco to tell the parentid.
*/
public static String getSocoParentIdKey() {
return CQ_PARENT_ID;
}
/**
* @return the key used in soco to tell the tally timestamp.
*/
public static String getSocoTallyTimestampKey() {
return CQ_TALLY_TIMESTAMP;
}
/**
* @return the key used in soco to tell modification date.
*/
public static String getSocoLastModifiedKey() {
return CQ_LAST_MODIFIED;
}
/**
* @return the key used in soco to tell if there are attachments.
*/
public static String getSchemaAttachmentKey() {
return AS_ATTACHMENT;
}
/**
* @return the key for a piece of ugc.
*/
public static String getSocoKey() {
return CQ_KEY;
}
/**
* @return the key used in soco to give the description associated with the ugc.
*/
public static String getSocoDescriptionKey() {
return CQ_DESCRIPTION;
}
/**
* Escape things that need to go to solr.
* @param base the original string
* @return the converted string.
*/
public static String escapeForSolr(final String base) {
return StringUtils.replace(base, ":", "\\:");
}
/**
* @return The prefix used to identify non dynamic user date properties.
*/
public static String getUserDatePrefix() {
return AS_CQ_SUBHASH_DATE;
}
/**
* @return The social base type.
*/
public static String getSchemaBaseType() {
return AS_BASE_TYPE;
}
/**
* @return The CQ base type.
*/
public static String getBaseType() {
return CQ_BASE_TYPE;
}
public static String getSchemaCQData() {
return AS_CQ_DATA;
}
/**
* @return The CQ translation folder name.
*/
public static String getTranslationFolderName() {
return CQ_MT_TRANSLATION;
}
/**
* Convert Calendar/Date to millis for storage.
* @param key Key to convert value for
* @param asMap The map
* @return milliseconds
*/
private static Long toSchemaDate(final String key, final Map asMap) {
final Object time = asMap.get(key);
if (time instanceof Date) {
return ((Date) time).getTime();
} else if (time instanceof Calendar) {
return ((Calendar) time).getTimeInMillis();
} else if (time == null) {
LOGGER.error("Date property {} doesn't exist or is null. Dropping it.", key);
return null;
}
LOGGER.error("Date property {} in unexpected format. Dropping it. Timestamp: {}. Class: {}", new Object[]{
key, time, time.getClass()});
return null;
}
/**
* Convert Calendar/Date to millis for storage.
* @param time Date/Calendar object
* @return milliseconds
*/
private static Long toSchemaDate(final Object time) {
if (time instanceof Date) {
return ((Date) time).getTime();
} else if (time instanceof Calendar) {
return ((Calendar) time).getTimeInMillis();
} else if (time == null) {
return null;
}
return null;
}
private static void spamFlagsToSchema(final Map cqMap, final Map asMap) {
final Boolean spam = (Boolean) cqMap.get(CQ_SPAM);
final Boolean approved = (Boolean) cqMap.get(CQ_APPROVED);
if (spam == null && approved == null) {
return;
}
if (spam != null) {
asMap.put(AS_APPROVED, !spam);
} else {
asMap.put(AS_APPROVED, approved);
}
}
private void setSolrId(final Map cqFormat, final String key, final Map asMap) {
final String solrId = (String) cqFormat.get(SOLR_ID);
if (solrId == null || solrId.isEmpty()) {
LOGGER.trace("Mandatory unique solr id field is missing for provider_id={}", key);
asMap.put(SOLR_ID, key);
}
}
/**
* Map from the cq data format to the format expected by the schema.
* @param cqFormat the incoming data. Note that this routine can change the contents of the map so that it is
* appropriate for caching. If you don't like that, make a copy of the map before sending it in.
* @param key the key for the data
* @return the mapped data
*/
public Map toSchema(final Map cqFormat, final String key) {
final Map asMap = new HashMap();
final Map cqDataMap = new HashMap();
final boolean bTranslationProcessingRequired = isTranslationProcessingRequired(key);
String strTranslationLanguageKey = null;
if (cqFormat.containsKey(CQ_TALLY_TIMESTAMP) && !cqFormat.containsKey(AbstractSchemaMapper.getSocoAddedKey())) {
if (!(cqFormat.get(CQ_TALLY_TIMESTAMP) instanceof Calendar)) { // tally timestamp is Calendar now
final String timestamp = (String) cqFormat.get(CQ_TALLY_TIMESTAMP);
final SimpleDateFormat dateFormat = new SimpleDateFormat(CQ_DATE_FORMAT);
try {
final Calendar cal = new GregorianCalendar();
cal.setTime(dateFormat.parse(timestamp));
cqFormat.put(AbstractSchemaMapper.getSocoAddedKey(), cal);
} catch (final java.text.ParseException e) {
LOGGER.error("Could not parse timestamp. Dropping it. Key: {}, timestamp: {}", key, timestamp);
}
} else {
cqFormat.put(AbstractSchemaMapper.getSocoAddedKey(), cqFormat.get(CQ_TALLY_TIMESTAMP));
}
}
spamFlagsToSchema(cqFormat, asMap);
for (final Entry entry : cqFormat.entrySet()) {
if (bTranslationProcessingRequired && TRANSLATION_TO_SCHEMA_KEYS.containsKey(entry.getKey())) {
if (strTranslationLanguageKey == null) {
// get the translation language
strTranslationLanguageKey = getTranslationLanguageFromKey(key);
}
final String strNewKey = TRANSLATION_TO_SCHEMA_KEYS.get(entry.getKey()) + strTranslationLanguageKey;
asMap.put(strNewKey, entry.getValue());
} else if (TOP_LEVEL_TO_SCHEMA_KEYS.containsKey(entry.getKey())) {
// if it is one of our known keys, put it in the top-level map
if (!entry.getKey().equals(CQ_APPROVED)) {
if (entry.getKey().equals(CQ_PUBLISH_DATE)) {
asMap.put(TOP_LEVEL_TO_SCHEMA_KEYS.get(entry.getKey()), toSchemaDate(entry.getValue()));
} else {
asMap.put(TOP_LEVEL_TO_SCHEMA_KEYS.get(entry.getKey()), entry.getValue());
}
}
} else {
if (entry.getKey().equals(CQ_SPAM)) {
continue;
}
// if a dynamic key, make it visible at top-level map to be indexed by solr
final Matcher matcher = PATTERN.matcher(entry.getKey());
if (matcher.matches()) {
final Object obj = entry.getValue();
if ((obj instanceof Calendar) || (obj instanceof Date)) {
asMap.put(entry.getKey(), toSchemaDate(entry.getKey(), cqFormat));
} else if ((obj instanceof Calendar[]) || (obj instanceof Date[])) {
LOGGER.debug("toSchema Cal/Date array {} - {}", entry.getKey(), entry.getValue());
final int len = ((Object[]) obj).length;
final Long[] milisArray = new Long[len];
for (int i = 0; i < len; i++) {
milisArray[i] = toSchemaDate(((Object[]) obj)[i]);
}
asMap.put(entry.getKey(), milisArray);
} else {
asMap.put(entry.getKey(), entry.getValue());
}
// an increment map is special. Have to go through it and put in
// appropriate $inc map (main or subhash).
//
} else if (CachingResourceProvider.INC.equals(entry.getKey()) && entry.getValue() instanceof Map) {
final Map incMap = (Map) entry.getValue();
final Map mainIncMap = new HashMap();
final Map subIncMap = new HashMap();
for (final Entry incEntry : incMap.entrySet()) {
if (TOP_LEVEL_TO_SCHEMA_KEYS.containsKey(incEntry.getKey())) {
mainIncMap.put(TOP_LEVEL_TO_SCHEMA_KEYS.get(incEntry.getKey()), incEntry.getValue());
} else {
final Matcher incMatcher = PATTERN.matcher(incEntry.getKey());
if (incMatcher.matches()) {
mainIncMap.put(incEntry.getKey(), incEntry.getValue());
} else {
subIncMap.put(incEntry.getKey(), incEntry.getValue());
}
}
}
if (!mainIncMap.isEmpty()) {
asMap.put(CachingResourceProvider.INC, mainIncMap);
}
if (!subIncMap.isEmpty()) {
cqDataMap.put(CachingResourceProvider.INC, subIncMap);
}
} else if (entry.getKey().indexOf(AS_VERBATIM_LANGUAGE_INDEX) == 0) {
LOGGER.trace("Adding {} to hash.", entry.getKey());
asMap.put(entry.getKey(), entry.getValue());
} else {
// Put it in the sub hash (not indexed in solr).
// This is common to ASRP and MSRP
LOGGER.trace("Adding {} to sub hash.", entry.getKey());
// Date/Calendar fields need special handling
final Object obj = entry.getValue();
final String fieldName = entry.getKey();
if ((obj instanceof Calendar) || (obj instanceof Date)) {
LOGGER.debug("Have date/calendar field: {}", fieldName);
final Long tstamp = toSchemaDate(fieldName, Collections.singletonMap(fieldName, obj));
if (tstamp != null) {
// Existing data may have these field names, so can't switch to the prefix scheme
if (SUBHASH_DATE_FIELDS.contains(fieldName)) {
cqDataMap.put(fieldName, tstamp);
} else {
cqDataMap.put(AbstractSchemaMapper.getUserDatePrefix() + fieldName, tstamp);
}
} else {
LOGGER.warn("Invalid date format for {}. Field ignored", fieldName);
}
} else if ((obj instanceof Calendar[]) || (obj instanceof Date[])) {
LOGGER.debug("Non dynamic date array {}", fieldName);
final int len = ((Object[]) obj).length;
final Long[] milisArray = new Long[len];
for (int i = 0; i < len; i++) {
milisArray[i] = toSchemaDate(((Object[]) obj)[i]);
}
cqDataMap.put(AbstractSchemaMapper.getUserDatePrefix() + fieldName, milisArray);
} else {
cqDataMap.put(fieldName, entry.getValue());
}
}
}
}
if (!cqDataMap.isEmpty()) {
asMap.put(AS_CQ_DATA, cqDataMap);
}
asMap.put(AS_PROVIDER_ID, key);
setSolrId(cqFormat, key, asMap);
if (getReportSuite() != null) {
asMap.put(AS_REPORT_SUITE, getReportSuite());
}
// timestamp (in millis) is required for AS. Add current time if not present.
// Don't want the 'dropping' error messages, so not using toSchemaDate.
Long timestamp = System.currentTimeMillis();
if (asMap.containsKey(AS_CREATED)) {
final Object time = asMap.get(AS_CREATED);
if (time instanceof Date) {
timestamp = ((Date) time).getTime();
} else if (time instanceof Calendar) {
timestamp = ((Calendar) time).getTimeInMillis();
}
}
asMap.put(AS_CREATED, timestamp);
finalizeSchemaMapping(cqFormat, asMap);
return asMap;
}
/**
* Check all entries in Translation schema map to find the right key whose value + language matches strInputkey
*/
private String getTranslationKeyEntry(final String strInputkey, final String strTranslationLanuage) {
// the last index should be equal to key length - translation language length
for (final Entry subEntry : TRANSLATION_TO_SCHEMA_KEYS.entrySet()) {
if (strInputkey.equals(subEntry.getValue() + strTranslationLanuage)) {
return subEntry.getKey();
}
}
return null;
}
/**
* Returns the translation language specified as the last name in the key path
* @param strLanguage Language
* @return String
*/
public static String getMutliLingualLanguageKey(String strLanguage) {
if (strLanguage.length() > 2) {
// if the language is more than 2 char code then we need to shorten it
// check if it is zh_cn or zh_tw, they are special case
if ("zh_tw".compareToIgnoreCase(strLanguage) == 0) {
strLanguage = "zh_TW";
} else if ("zh_cn".compareToIgnoreCase(strLanguage) == 0) {
strLanguage = "zh_CN";
} else {
strLanguage = strLanguage.substring(0, 2);
}
}
return strLanguage;
}
/**
* Returns the translation language specified as the last name in the key
* @param key language Key
* @return String
*/
private String getTranslationLanguageFromKey(final String key) {
final String[] paths = key.split("/");
if (paths != null && paths.length > 0) {
return getMutliLingualLanguageKey(paths[paths.length - 1]);
}
return "";
}
/**
* This method first checks if translation schema map is empty or not, and then check if key provided has
* translation as second last path, and returns true otherwise false
* @param key
*/
private boolean isTranslationProcessingRequired(final String key) {
// first check if translation schema has keys
if (!TRANSLATION_TO_SCHEMA_KEYS.isEmpty() && !StringUtils.isEmpty(key)) {
// now check that key is of type /translation/abc
final String[] paths = key.split("/");
if (paths != null && paths.length > 1) {
final String strSecondLastPath = paths[paths.length - 2];
return getTranslationFolderName().equals(strSecondLastPath);
}
}
return false;
}
/**
* Subclasses can implement this to muck with the mapping before it is actually committed. This is useful for the
* little nitty differences between impls.
* @param socoData the data in the SoCo format
* @param schemaMap the data to be committed (in the common schema)
*/
public abstract void finalizeSchemaMapping(Map socoData, Map schemaMap);
/**
* Format a Map using the keys expected by the schema.
* @param data the data to be uploaded
* @param key the key for the attachment
* @return the data with the keys expected by the schema
* @throws IOException on failure
*/
public Map toAttachmentSchema(final Map data, final String key)
throws IOException {
final Map resultMap = new HashMap();
for (final Entry entry : data.entrySet()) {
if (!CQ_ATTACHMENT_KEYS.contains(entry.getKey())) {
resultMap.put(entry.getKey(), entry.getValue());
}
}
resultMap.put(AS_PROVIDER_ID, key);
resultMap.put(AS_CONTENT_TYPE, data.get(CQ_CONTENT_TYPE));
resultMap.put(AS_ATTACHMENT, data.get(CQ_ATTACHMENT));
resultMap.remove(AS_PARENT_ID);
return resultMap;
}
private static void spamFlagsFromSchema(final Map cqMap, final Map asMap) {
final Boolean approved = (Boolean) asMap.remove(AS_APPROVED);
if (approved == null) {
return;
}
cqMap.put(CQ_SPAM, !approved);
cqMap.put(CQ_APPROVED, approved);
}
/**
* Return a Calendar object from a date value in the schema format. If value is an invalid date format (perhaps
* from a legacy format we no longer know about or bugs from previous coding), return the current time as a place
* holder.
* @param key the key to convert
* @param asDate the original date, in schema format
* @return the converted date
*/
public static Calendar fromSchemaDate(final String key, final Object asDate) {
final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
Date date = null;
try {
if (asDate instanceof String) {
LOGGER.error("Legacy format data from schema. Key: {}, String date: {}", key, asDate);
date = dateFormat.parse((String) asDate);
} else if (asDate instanceof Long) {
date = new Date((Long) asDate);
} else if (asDate instanceof Integer) {
date = new Date((Integer) asDate);
} else if (asDate instanceof Double) {
date = new Date(((Double) asDate).longValue());
} else if (asDate instanceof Float) {
date = new Date(((Float) asDate).longValue());
} else if (asDate instanceof Calendar) {
return (Calendar) asDate;
} else if (asDate instanceof Date) {
date = (Date) asDate;
} else {
LOGGER.error("Unknown date format. Using current time. Key: {}, Date: {}. Type: "
+ (asDate == null ? "null" : asDate.getClass().toString()), key, asDate);
}
} catch (final ParseException e) {
LOGGER.error("Could not parse date. Using current time. Key: {}, Date: {}", key, asDate);
}
final Calendar cal = new GregorianCalendar();
if (date != null) {
cal.setTime(date);
}
return cal;
}
/**
* Convert the data from schema into the format expected by SoCo.
* @param schemaData the data to be converted
* @return the mapped data
*/
public Map fromSchema(final Map schemaData) {
LOGGER.debug("About to map from schema: {}", schemaData);
final Map asFormatCopy = new HashMap(schemaData);
final Map cqMap = new HashMap();
spamFlagsFromSchema(cqMap, asFormatCopy);
final boolean bTranslationMappingRequired =
isTranslationProcessingRequired((String) schemaData.get(AS_PROVIDER_ID));
String strTranslationLanuageKey = null;
for (final Entry entry : asFormatCopy.entrySet()) {
if (TOP_LEVEL_TO_CQ_KEYS.containsKey(entry.getKey())) {
if (entry.getKey().equals(AS_PUBLISH_DATE)) {
cqMap.put(TOP_LEVEL_TO_CQ_KEYS.get(entry.getKey()),
fromSchemaDate(entry.getKey(), entry.getValue()));
} else {
cqMap.put(TOP_LEVEL_TO_CQ_KEYS.get(entry.getKey()), entry.getValue());
}
// Non indexed fields, extract out of the sub hash
} else if (entry.getKey().equals(AS_CQ_DATA)) {
@SuppressWarnings("unchecked")
final Map cqData = (Map) entry.getValue();
// Handle date fields in sub hash
for (final Entry subEntry : cqData.entrySet()) {
final String fieldName = subEntry.getKey();
// The old fixed list that wasn't prefixed (jcr:modified etc.)
if (SUBHASH_DATE_FIELDS.contains(subEntry.getKey())) {
final Calendar cal =
fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), subEntry.getValue());
cqMap.put(fieldName, cal);
continue;
}
// If date prefix present, convert to Calendar.
if (StringUtils.startsWith(fieldName, AbstractSchemaMapper.getUserDatePrefix())) {
final String realName =
StringUtils.remove(fieldName, AbstractSchemaMapper.getUserDatePrefix());
final Object datesObj = subEntry.getValue();
// Single date
if (datesObj instanceof Long) {
final Calendar cal =
fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), subEntry.getValue());
cqMap.put(realName, cal);
continue;
}
// Array of dates. Comes back as array from AS, a List from Mongo
// Changing Mongo code to return array would break attachment handling.
Long[] milisArray;
if (datesObj instanceof List) {
milisArray = ((List) datesObj).toArray(new Long[((List) datesObj).size()]);
} else {
milisArray = (Long[]) datesObj;
}
// Convert to Calendar
final int len = milisArray.length;
final Calendar[] calArray = new Calendar[len];
for (int i = 0; i < len; i++) {
calArray[i] = fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), milisArray[i]);
}
cqMap.put(realName, calArray);
} else {
cqMap.put(fieldName, subEntry.getValue());
}
}
} else {
boolean bProcessingDone = false;
if (bTranslationMappingRequired) {
// get LanguageKey first of all
if (strTranslationLanuageKey == null) {
strTranslationLanuageKey =
getTranslationLanguageFromKey((String) schemaData.get(AS_PROVIDER_ID));
}
// get a language mapping first of all
final String strNewKeyEntry = getTranslationKeyEntry(entry.getKey(), strTranslationLanuageKey);
if (!StringUtils.isEmpty(strNewKeyEntry)) {
cqMap.put(strNewKeyEntry, entry.getValue());
bProcessingDone = true;
}
}
if (!bProcessingDone) {
// Dynamic date fields have to be converted out.
if (entry.getKey().endsWith("_dt")) {
final Calendar cal =
fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), entry.getValue());
cqMap.put(entry.getKey(), cal);
} else if (entry.getKey().endsWith("_dts")) {
LOGGER.debug("From schema dynamic date array Long to Calendar {}", entry.getKey());
final Long[] miliDates = (Long[]) entry.getValue();
final Calendar[] calArray = new Calendar[miliDates.length];
for (int i = 0; i < miliDates.length; i++) {
final Calendar cal =
fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), miliDates[i]);
calArray[i] = cal;
}
cqMap.put(entry.getKey(), calArray);
} else {
cqMap.put(entry.getKey(), entry.getValue());
}
}
}
}
// filter out Adobe Cloud Storage keys.
cqMap.remove(AS_REPORT_SUITE);
cqMap.remove(AS_AUTHOR_CI);
cqMap.remove(AS_AUTHOR_DISPLAY_NAME_CI);
cqMap.remove(AS_ID);
cqMap.remove(AS_COMPANY_ID);
cqMap.remove(AS_FEATURES);
if (schemaData.containsKey(AS_PROVIDER_ID)) {
cqMap.put(CQ_KEY, schemaData.get(AS_PROVIDER_ID));
}
// Dates (stored as millis) need to be mapped back out into calendar objects
if (cqMap.containsKey(CQ_ADDED)) {
final Calendar cal = fromSchemaDate((String) schemaData.get(AS_PROVIDER_ID), cqMap.get(CQ_ADDED));
cqMap.put(CQ_ADDED, cal);
}
// A little hack to keep the world moving. We moved from resource_type
// to resource_type_s, title to title_t, and root_id to thread_id_s,
// but all the data in the database still has resource_type. Remove this when we get a clean database..
if (!cqMap.containsKey(CQ_RESOURCE_TYPE)) {
if (schemaData.containsKey("resource_type")) {
LOGGER.error("Legacy format data from AS. Key: {}, resource_type: {}",
schemaData.get(AS_PROVIDER_ID), schemaData.get("resource_type"));
cqMap.put(CQ_RESOURCE_TYPE, schemaData.get("resource_type"));
}
}
if (!cqMap.containsKey(CQ_TITLE)) {
if (schemaData.containsKey("title")) {
LOGGER.error("Legacy format data from AS. Key: {}, title: {}", schemaData.get(AS_PROVIDER_ID),
schemaData.get("title"));
cqMap.put(CQ_TITLE, schemaData.get("title"));
}
}
if (!cqMap.containsKey(CQ_ROOT_COMMENT_SYSTEM)) {
if (schemaData.containsKey("root_id")) {
LOGGER.error("Legacy format data from AS. Key: {}, root_id: {}", schemaData.get(AS_PROVIDER_ID),
schemaData.get("root_id"));
cqMap.put(CQ_ROOT_COMMENT_SYSTEM, schemaData.get("root_id"));
}
}
LOGGER.debug("Completed mapping from schema: {}", cqMap);
return cqMap;
}
/**
* Convert attachment data from the schema to what SoCo expects.
* @param input the data in the schema
* @return the data in the SoCo format
*/
public Map fromAttachmentSchema(final Map input) {
if (input.isEmpty()) {
return input;
}
input.remove("md5");
input.remove("documentstore_id");
input.remove("attachment_id");
final Map resultMap = new HashMap();
for (final Entry entry : input.entrySet()) {
if (!AS_ATTACHMENT_KEYS.contains(entry.getKey())) {
resultMap.put(entry.getKey(), entry.getValue());
}
}
resultMap.put(CQ_KEY, input.get(AS_PROVIDER_ID));
resultMap.put(CQ_CONTENT_TYPE, input.get(AS_CONTENT_TYPE));
// convert the upload date to creation date
resultMap.put(
CQ_ADDED,
fromSchemaDate(AbstractSchemaMapper.getSchemaAttachmentUploadDateKey(),
input.get(AbstractSchemaMapper.getSchemaAttachmentUploadDateKey())));
// make sure content length is a Long since AS returns an Integer
if (resultMap.get(AS_ATTACHMENT_LENGTH) != null) {
resultMap.put(AS_ATTACHMENT_LENGTH, ((Number) resultMap.get(AS_ATTACHMENT_LENGTH)).longValue());
}
return resultMap;
}
/**
* Get the schema mapping of a SoCo field. Needed to map CQ fields in a query to Solr fields, since we don't have
* information about the field type to be able to come up with the Solr name used when adding to index. Custom
* fields have Solr schema names, so if not found, assume it is a custom dynamic field.
* @param cqKey The CQ name of a field
* @return The Solr index name for the CQ field or the input if not mapped (dynamic field name)
*/
public String toSchemaKey(final String cqKey) {
if (TOP_LEVEL_TO_SCHEMA_KEYS.containsKey(cqKey)) {
return TOP_LEVEL_TO_SCHEMA_KEYS.get(cqKey);
} else {
return cqKey;
}
}
/**
* Get the CQ mapping of a schema field. Needed to map schema key to CQ field. For facet fields etc, where we
* don't have a map, just a string.
* @param schemaKey The schema name of a field
* @return The CQ field name or the input if not mapped (dynamic field name)
*/
public String fromSchemaKey(final String schemaKey) {
if (TOP_LEVEL_TO_CQ_KEYS.containsKey(schemaKey)) {
return TOP_LEVEL_TO_CQ_KEYS.get(schemaKey);
} else {
return schemaKey;
}
}
/**
* Get the schema field name(s) associated with a given SoCo field.
* @param mltField the SoCo field
* @return the list of schema field names
*/
public List toSchemaKeys(final String mltField) {
final List override = mapToMultipleValues(mltField);
if (override != null) {
return override;
}
final String ret = TOP_LEVEL_TO_SCHEMA_KEYS.get(mltField);
if (ret == null) {
return Arrays.asList(mltField);
}
return Arrays.asList(ret);
}
/**
* Each Schema mapper class need to implement this method. Return list of verbatim supported
* @return String[]
*/
protected abstract String[] getASVerbatimList();
private List mapToMultipleValues(final String mltField) {
if (mltField.equals(getSocoDescriptionKey())) {
return new ArrayList(Arrays.asList(getASVerbatimList()));
}
return null;
}
/**
* Convert a lucene query to Solr query Maps the CQ names to Solr names. Handles the path wild card since Solr
* does not find strings ending in a wildcard if it does not start with a wildcard.
* @param luceneQuery the lucene query
* @return the solr query
*/
public String luceneToSolr(final String luceneQuery) {
LOGGER.debug("Lucene query: {}", luceneQuery);
final StringBuilder sb = new StringBuilder().append(luceneQuery);
// Full text queries come in with a :fulltext: which needs to be removed
replaceAll(sb, ":fulltext:", "");
/*
* Avoid mapping in search string by replacing only the strings that end with a :
*/
// For the path query, it needs the wildcard at start, but we already do that in the query construction
replaceAll(sb, CQ_SEARCH_PATH + ":", AS_PROVIDER_ID + ":");
// Lucene query coming in has as tags, not cq:tags, so standard mapping will not work
// Wildcard for now
replaceAll(sb, CQ_SEARCH_TAG_NAME + ":", AS_CQTAGS + ":");
// :parent can come when a query is using PathConstraintType.IsDescendantNode
replaceAll(sb, CQ_SEARCH_PARENT_ID + ":", AS_PARENT_ID + ":");
// Solr does not like the ranges social-collections lucene parsing gives us.
replaceAll(sb, "[ TO", "[* TO");
replaceAll(sb, " TO \uFFFF]", " TO *]");
LOGGER.debug("Replaced range limits {}", sb.toString());
// Just the regular CQ-AS mappings.
for (final Entry entry : TOP_LEVEL_TO_SCHEMA_KEYS.entrySet()) {
final String cqKey = entry.getKey();
final List override = mapToMultipleValues(cqKey);
final String solrKey = entry.getValue();
if (override != null) {
// TODO: Proper handling, adding the rest of the AS_VERBATIMS
replaceAll(sb, cqKey + ":", override.get(0) + ":");
} else {
replaceAll(sb, cqKey + ":", solrKey + ":");
}
}
final String solrQuery = sb.toString().trim();
//This logic is needed to take care of keyword search in calendar addresses because of adresses being strings
String calendarSolrQuery = null;
if (solrQuery.indexOf(AS_ADDRESS) >= 0) {
int addressStartIndex = solrQuery.indexOf(AS_ADDRESS);
int addressFinalIndex = addressStartIndex + solrQuery.substring(addressStartIndex).indexOf(") ");
String keyParam = solrQuery.substring(addressStartIndex, addressFinalIndex).trim();
String searchString = keyParam.substring(keyParam.indexOf(":(") + 2);
String prefixString = solrQuery.substring(0, addressStartIndex);
//Process key
String []literalArray = searchString.split("\\s+");
StringBuilder key = new StringBuilder();
for (String literal : literalArray) {
key.append(literal)
.append("\\")
.append(" ");
}
//Remove extraneous "\ " from the end of search key
String searchKey = key.toString().trim();
searchKey = searchKey.substring(0, searchKey.length() - 1);
//Create new solr query with processed key for address
calendarSolrQuery = prefixString.concat(AS_ADDRESS).concat(":(").concat("*").concat(searchKey).concat("*").concat(solrQuery.substring(addressFinalIndex));
LOGGER.debug("Solr query: {}", calendarSolrQuery);
return calendarSolrQuery;
} else {
LOGGER.debug("Solr query: {}", solrQuery);
return solrQuery;
}
}
/**
* Replace all occurrences of a string in a StringBuilder.
* @param builder the StringBuilder (modified)
* @param from source string
* @param to replacement string
*/
private static void replaceAll(final StringBuilder builder, final String from, final String to) {
int index = builder.indexOf(from);
while (index != -1) {
builder.replace(index, index + from.length(), to);
index += to.length(); // Move to the end of the replacement
index = builder.indexOf(from, index);
}
}
/**
* Convert to a format solr likes.
* @param cqFormat the cq format data
* @param key the key to convert
* @return Map the converted data
*/
public abstract Map toSolrSchema(final Map cqFormat, final String key);
/**
* Checks if the 'key' is valid key that can be indexed in social-indexing engine (Solr,ElasticSearch,etc)
* @param key Key
* @return boolean
*/
public boolean isIndexable(String key) {
if (key.equals(SOLR_ID) || key.equals(AS_REPORT_SUITE)) {
return true;
}
// 2. check if its one of the out-of-box solr fields defined for communities
boolean matchesOutOfBoxFieldsInSolr = TOP_LEVEL_TO_CQ_KEYS.containsKey(key);
if (matchesOutOfBoxFieldsInSolr) {
return true;
} else {
// 1. check if given key matches any of the dynamic field patterns
final Matcher incMatcher = PATTERN.matcher(key);
return incMatcher.matches();
}
}
}