com.day.cq.wcm.commons.ReferenceSearch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/* * Copyright 1997-2008 Day Management AG * Barfuesserplatz 6, 4001 Basel, Switzerland * All Rights Reserved. * * This software is the confidential and proprietary information of * Day Management AG, ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Day. */ package com.day.cq.wcm.commons; import com.day.cq.commons.jcr.JcrConstants; import com.day.cq.commons.predicate.AbstractResourcePredicate; import com.day.cq.commons.predicates.ResourcePredicate; import com.day.cq.search.PredicateGroup; import com.day.cq.search.Query; import com.day.cq.search.QueryBuilder; import com.day.cq.search.result.SearchResult; import com.day.cq.wcm.api.NameConstants; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; import com.day.cq.wcm.api.WCMException; import org.apache.jackrabbit.util.Text; import org.apache.sling.api.SlingException; import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; import org.apache.sling.jcr.api.SlingRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.BitSet; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.sling.api.resource.Resource.RESOURCE_TYPE_NON_EXISTING; /** *
if not matches */ protected String rewrite(String value, String from, Pattern p, String to, String escTo) { return rewrite(value, from, p, to, escTo, null, false); } /** * Internal rewrite method. * @param value the property value * @param from original path * @param p the replacement pattern * @param to to path * @param escTo escaped to path * @param resolver ResourceResolver for checking valid href * @param withoutChildNodes ifReferenceSearch
provides methods that search references to * resources (e.g. a path in a property to an image) */ public class ReferenceSearch { private static final String HREF_REGEX = "(href\\s*=\\s*)([\"']/*[^\"']*[\"'])"; private SlingRepository repository; private boolean isValidatePage = false; /** * default logger */ private static final Logger log = LoggerFactory.getLogger(ReferenceSearch.class); /** * search root */ private String searchRoot = "/"; /** * exact */ private boolean exact = false; /** * hollow */ private boolean hollow = false; /** * reference limit per page */ private int maxReferencesPerPage = -1; /** * Resource predicate, can be set to filter results based on a resource predicate {@link ResourcePredicate} */ private ResourcePredicate resourcePredicate = null; /** * Resource predicate, can be set to filter results based on a resource predicate {@link AbstractResourcePredicate} */ private AbstractResourcePredicate oldResourcePredicate = null; /** * Default limit for pagination of results is 100. */ private static final int DEFAULT_LIMIT = 100; /** * Default offset for pagination of results is 0. */ private static final int DEFAULT_START_OFFSET = 0; /** * Default Index Tag used for Reference Search in Sites */ static final String DEFAULT_SITES_INDEX_TAG = "wcmReferenceSearch"; /** * @deprecated The repository was used by the implementation to create an administrative session in * {@link #adjustReferences(ResourceResolver, String, String, String[])}. Instead, specify a resource resolver * which is sufficiently authorized to adjust references at the desired locations. * @param repository The repository * @return this */ public ReferenceSearch setRepository(SlingRepository repository) { this.repository = repository; return this; } /** * Returns the search root. default is '/' * @return the search root. */ public String getSearchRoot() { return searchRoot; } /** * Sets the search root * @param searchRoot the search root * @return this */ public ReferenceSearch setSearchRoot(String searchRoot) { if (searchRoot == null || searchRoot.equals("")) { this.searchRoot = "/"; } else { this.searchRoot = searchRoot; } return this; } /** * Returns the shallow flag. * @return the shallow flag. * @see #setExact(boolean) */ public boolean isExact() { return exact; } /** * Sets theexact
flag. Iftrue
only exact * references are searched (and replaced). otherwise also references to * child resources are included. * * @param exacttrue
if perform an exact search * @return this; */ public ReferenceSearch setExact(boolean exact) { this.exact = exact; return this; } /** * Returns thehollow
flag. * @return thehollow
flag. * @see #setHollow(boolean) */ public boolean isHollow() { return hollow; } /** * Sets thehollow
flag. Iftrue
* the returned info will contain only properties of the page * and not the page object itself. * @param hollowtrue
if perform a hollow search * @return this; */ public ReferenceSearch setHollow(boolean hollow) { this.hollow = hollow; return this; } /** * Returns the maximum number of references that can be added to a page info. * @return the reference limit per page. * @see #setMaxReferencesPerPage(int) */ public int getMaxReferencesPerPage() { return maxReferencesPerPage; } /** * Sets the value ofmaxReferencesPerPage
. * The maximum number of references that can be added to a page info. * @param maxReferencesPerPage The maximum number of references that can be added to a page info. * @return this; */ public ReferenceSearch setMaxReferencesPerPage(int maxReferencesPerPage) { this.maxReferencesPerPage = maxReferencesPerPage; return this; } /** * Set theresourcePredicate
, this predicate is evaluated for * each search result and can be used to filter results. * * @param resourcePredicate an instance of {@link ResourcePredicate} * @return this; */ public ReferenceSearch setPredicate(ResourcePredicate resourcePredicate) { this.resourcePredicate = resourcePredicate; return this; } /** * Set theresourcePredicate
, this predicate is evaluated for * each search result and can be used to filter results. * * @param resourcePredicate an instance of {@link AbstractResourcePredicate} * @return this; * @deprecated Use {@link #setPredicate(ResourcePredicate)} instead. */ @Deprecated public ReferenceSearch setPredicate(AbstractResourcePredicate resourcePredicate) { this.oldResourcePredicate = resourcePredicate; return this; } /** * Construct a query based on the input perm and fetch the result by using cq queryBuilder. * * @param resolver ResourceResolver * @param root Path restriction for the query * @param pathList List of path strings to be matched in the query * @param offset offset * @param limit limit on number of results per request * @param nodeType Node type restriction to be used in the query. * @return */ private IteratorbuildQueryAndFetchResults(ResourceResolver resolver, String root, List pathList, int offset, int limit, String nodeType) { QueryBuilder queryBuilder = resolver.adaptTo(QueryBuilder.class); Map map = new HashMap<>(); if (offset < 0) { offset = DEFAULT_START_OFFSET; } if (limit <= 0) { limit = DEFAULT_LIMIT; } if (!root.trim().isEmpty()) { map.put("path", root); } if (nodeType != null && !nodeType.trim().isEmpty()) { map.put("type", "cq:Page"); } map.put("p.guessTotal", "true"); map.put("p.offset", String.valueOf(offset)); map.put("p.limit", String.valueOf(limit)); map.put("p.indexTag", DEFAULT_SITES_INDEX_TAG); int index = 1; for (String resourcePath : pathList) { if (!resourcePath.trim().isEmpty()) { String predicateKey = String.format("group.%s_fulltext", index); map.put(predicateKey, resourcePath); index++; } } map.put("group.p.or", "true"); Query query = queryBuilder.createQuery(PredicateGroup.create(map), resolver.adaptTo(Session.class)); SearchResult result = query.getResult(); return result.getResources(); } /** * This Method is to get page references of the given resource path. * This API is recommended to use where reference count is required or where only page references are need to prepare reports. * This API should't be used to adjust the references as it doesn't * provide exact path to the descendant node of a page which actually hold the given resource path. * @param resolver ResourceResolver. * @param path It's given path for which page references need to be fetch. * @param limit upper limit on size of result in current request. * @param offset offset. * @return List of Pages having reference to the given resource path. */ public List findPageReferencesForResource(ResourceResolver resolver, String path, int limit, int offset) { if (path == null) { return Collections.emptyList(); } List pageReferences = new ArrayList (); String root = searchRoot.equals("/") ? "" : searchRoot; PageManager manager = resolver.adaptTo(PageManager.class); List patternList = new ArrayList (); List pathList = new ArrayList (); prepareQueryInputs(patternList, pathList, path); Iterator resources = buildQueryAndFetchResults(resolver, root, pathList, offset, limit, NameConstants.NT_PAGE); resources.forEachRemaining(res -> { Page page = manager.getContainingPage(res); pageReferences.add(page); if (resourcePredicate != null && !resourcePredicate.test(res)) { pageReferences.remove(page); } else if (oldResourcePredicate != null && !oldResourcePredicate.evaluate(res)) { pageReferences.remove(page); } }); return pageReferences; } /** * This method is to get page references of the given resource path. * This API provides exact location of the child node of a page which actually holds the given resource path. * This API is recommended to use where reference are need to be adjusted. * @param resolver ResourceResolver. * @param path It's given path for which page references need to be fetch. * @param limit upper limit on size of result in current request. * @param offset offset. * @return Map having resorce path a key and Info object as key. Info Holds information about the search results. */ public Map search(ResourceResolver resolver, String path, int limit, int offset) { if (path == null) { return Collections.emptyMap(); } String root = searchRoot.equals("/") ? "" : searchRoot; PageManager manager = resolver.adaptTo(PageManager.class); Map infos = new HashMap (); List patternList = new ArrayList (); List pathList = new ArrayList (); prepareQueryInputs(patternList, pathList, path); // nodetype is intentionally set to null in the input parameters of // buildQueryAndFetchResults, as Consumers // expect a complete path to the referring property of the page. Iterator resources = buildQueryAndFetchResults(resolver, root, pathList, offset, limit, null); processResultset(resources, manager, infos, patternList); return infos; } /** * Prepare input string to be used as fultext search perm * @param patternList pattern to be matched * @param pathList input string to be used as fultext search perm * @param path resource path for wich references to be fetched */ private void prepareQueryInputs(List patternList, List pathList, String path) { String qHtmlEncodeescPath; Pattern htmlEncodedescPattern; Pattern escUppercasePattern; String qEscUppercasePath; Pattern pattern = getSearchPattern(path); String qPath = escapeIllegalXpathSearchChars(path); patternList.add(pattern); pathList.add(qPath); // also search for escaped path, if contains special characters. String escPath = Text.escapePath(path); if (!escPath.equals(path)) { Pattern escPattern = getSearchPattern(escPath); String qEscPath = escapeIllegalXpathSearchChars(escPath); patternList.add(escPattern); pathList.add(qEscPath); // RTE in Classic UI and in Touch UI post CQ-4241224: all hex upper case encoded escPath = escapePathUsingUpperCaseHex(path); escUppercasePattern = getSearchPattern(escPath); qEscUppercasePath = escapeIllegalXpathSearchChars(escPath); patternList.add(escUppercasePattern); pathList.add(qEscUppercasePath); // RTE in Touch UI pre CQ-4241224: & encoded as HTML entity; others hex upper // case encoded if (escPath.contains("%26")) { escPath = escPath.replaceAll("%26", "&"); htmlEncodedescPattern = getSearchPattern(escPath); qHtmlEncodeescPath = escapeIllegalXpathSearchChars(escPath); patternList.add(htmlEncodedescPattern); pathList.add(qHtmlEncodeescPath); } } } /** * Searches for references to the given path. * @param resolver the resource resolver * @param path the path to search for * @return reference infos * @deprecated use Search(ResourceResolver resolver, String path, String limit, String offset) */ public Map search(ResourceResolver resolver, String path) { if (path == null) { return Collections.emptyMap(); } String root = searchRoot.equals("/") ? "" : searchRoot; PageManager manager = resolver.adaptTo(PageManager.class); Map infos = new HashMap (); Pattern pattern = getSearchPattern(path); String qPath = escapeIllegalXpathSearchChars(path); String query = String.format("%s//*[jcr:contains(., '\"%s\"')]", root, qPath); search(resolver, manager, infos, pattern, query); // also search for escaped path, if contains special characters String escPath = Text.escapePath(path); if (!escPath.equals(path)) { // already rewritten values: all hex lower case encoded Pattern escPattern = getSearchPattern(escPath); String qEscPath = escapeIllegalXpathSearchChars(escPath); query = String.format("%s//*[jcr:contains(., '%s')]", root, qEscPath); search(resolver, manager, infos, escPattern, query); // RTE in Classic UI and in Touch UI post CQ-4241224: all hex upper case encoded escPath = escapePathUsingUpperCaseHex(path); escPattern = getSearchPattern(escPath); qEscPath = escapeIllegalXpathSearchChars(escPath); query = String.format("%s//*[jcr:contains(., '%s')]", root, qEscPath); search(resolver, manager, infos, escPattern, query); // RTE in Touch UI pre CQ-4241224: & encoded as HTML entity; others hex upper // case encoded if (escPath.contains("%26")) { escPath = escPath.replaceAll("%26", "&"); escPattern = getSearchPattern(escPath); qEscPath = escapeIllegalXpathSearchChars(escPath); query = String.format("%s//*[jcr:contains(., '%s')]", root, qEscPath); search(resolver, manager, infos, escPattern, query); } } return infos; } private void filterResultset(Map infos) { for (Iterator > entries = infos.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = entries.next(); if (entry.getValue().getProperties().isEmpty()) { entries.remove(); } else { // filter based on predicate if (resourcePredicate != null || oldResourcePredicate != null) { if (entry.getValue().page != null) { Resource pageResource = entry.getValue().page.adaptTo(Resource.class); if (pageResource != null) { if (resourcePredicate != null && !resourcePredicate.test(pageResource)) { entries.remove(); } else if (oldResourcePredicate != null && !oldResourcePredicate.evaluate(pageResource)) { entries.remove(); } } } } } } } private String escapePathUsingUpperCaseHex(String string) { try { BitSet validChars = Text.URISaveEx; char escape = '%'; final char[] hexTable = "0123456789ABCDEF".toCharArray(); byte[] bytes = string.getBytes("utf-8"); StringBuilder out = new StringBuilder(bytes.length); for (byte aByte : bytes) { int c = aByte & 0xff; if (validChars.get(c) && c != escape) { out.append((char) c); } else { out.append(escape); out.append(hexTable[(c >> 4) & 0x0f]); out.append(hexTable[(c) & 0x0f]); } } return out.toString(); } catch (UnsupportedEncodingException e) { throw new InternalError(e.toString()); } } private void search(ResourceResolver resolver, PageManager manager, Map infos, Pattern pattern, String query) { // adding index tag in the query query = query.concat(" option(index tag " + DEFAULT_SITES_INDEX_TAG + ")"); log.debug("Searching for references using: {}", query); Iterator iter = null; try { iter = resolver.findResources(query, javax.jcr.query.Query.XPATH); } catch (SlingException e) { log.warn("error finding resources", e); return; } List patternList = new ArrayList (); patternList.add(pattern); processResultset(iter, manager, infos, patternList); } /** * process the search results to check if the entries are not empty and match with the pattern and resourcePredicate. * @param iter iterator for result set * @param manager PageManager instance * @param infos Info * @param patternList pattern to be matched */ private void processResultset(Iterator iter, PageManager manager, Map infos, List patternList) { log.debug("processing the search results and building the result set"); // process the search results and build the result set while (iter.hasNext()) { Resource res = iter.next(); Page page = manager.getContainingPage(res); if (page != null) { Info info = infos.get(page.getPath()); if (info == null) { info = new Info(page, hollow); infos.put(page.getPath(), info); } try { // analyze the properties of the resource Node node = res.adaptTo(Node.class); for (PropertyIterator pIter = node.getProperties(); pIter.hasNext();) { // don't add properties any further if limit is exceeded if (getMaxReferencesPerPage() >= 0 && info.getProperties().size() >= getMaxReferencesPerPage()) { break; } Property p = pIter.nextProperty(); // only check string and name properties if (p.getType() == PropertyType.STRING || p.getType() == PropertyType.NAME) { if (p.isMultiple()) { for (Value v : p.getValues()) { String value = v.getString(); if (isPatternMatched(patternList, value)) { info.addProperty(p.getPath()); break; } } } else { String value = p.getString(); if (isPatternMatched(patternList, value)) { info.addProperty(p.getPath()); } } } } } catch (RepositoryException e) { log.error("Error while accessing " + res.getPath(), e); } } } // filter out those infos that are empty filterResultset(infos); } private boolean isPatternMatched(List patternList, String value) { boolean isPatternMatched = false; for(Pattern pattern : patternList) { isPatternMatched = pattern.matcher(value).find(); if(isPatternMatched) { return isPatternMatched; } } return isPatternMatched; } /** * Adjusts all references to
* *path
todestination
* in the pages specified byrefPaths
. If {@link #isExact()} * istrue
only exact references topath
are * adjusted, otherwise all references to child resources are adjusted, too.The resource resolver needs to have sufficient permissions (i.e.
* * @param resolver resolver to operate on. * @param path source path * @param destination destination path * @param refPaths paths of pages to be adjusted * @return collection of path to properties that were adjusted */ public Collectionjcr:read
and *rep:alterProperties
) on the nodes containing references.adjustReferences(ResourceResolver resolver, String path, String destination, String[] refPaths) { if (refPaths == null) { return Collections.emptyList(); } Set adjusted = new HashSet (); for (String p: refPaths) { Resource r = resolver.getResource(p); if (r == null) { log.warn("Given path does not address a resource: {}", p); continue; } Page page = r.adaptTo(Page.class); if (page == null) { log.warn("Given path does not address a page: {}", p); } Resource content = page != null ? page.getContentResource() : null; if (content == null) { log.warn("Given page does not have content: {}", p); } try { //Can be a case of complex asset. Node node = content != null ? content.adaptTo(Node.class) : r.adaptTo(Node.class); adjusted.addAll(adjustReferences(node, path, destination)); // CQ5-32249 - touch the pages outside of this loop to avoid an inefficient O(n^2) algorithm } catch (RepositoryException e) { log.error("Error while adjusting references on " + r.getPath(), e); } // #22466 - moving pages does not take into account usergenerated content try { String adjustedUGCPath = adjustUserGeneratedContentReference(r, path, destination); if (adjustedUGCPath != null) { adjusted.add(adjustedUGCPath); log.info("Adjusted user generated content path {}.", adjustedUGCPath); } } catch (Exception e) { log.error("Error while adjusting user generated references on " + r.getPath(), e); } } // CQ5-32249 - do the touch calls after the above loop to avoid an inefficient O(n^2) algorithm PageManager pm = resolver.adaptTo(PageManager.class); // #38440 - touch the pages that were adjusted for (final String pathOfAdjusted : adjusted) { final Resource adjustedResource = resolver.getResource(pathOfAdjusted); if (null != adjustedResource) { final Page adjustedPage = pm.getContainingPage(adjustedResource); if (null != adjustedPage) { try { pm.touch(adjustedPage.adaptTo(Node.class), true, Calendar.getInstance(), false); } catch (WCMException e) { log.error("could not update last modified on adjusted page [{}]: ", adjustedPage.getPath(), e); } } //CQ-4316424 - touch the contentFragments that were adjusted touchIfContentFragmentReference(pathOfAdjusted, resolver); } } // save changes try { resolver.commit(); } catch (PersistenceException e) { log.error("Error while adjusting references.", e); } return adjusted; } /** * @param adjustedPath path for which reference is adjusted * @param resolver resolver to operate on. */ private void touchIfContentFragmentReference(String adjustedPath, ResourceResolver resolver) { int lastIndexOfDelimiter = adjustedPath.lastIndexOf(searchRoot); String possibleCFRefPath = adjustedPath.substring(0, lastIndexOfDelimiter); Resource adjustedResource = resolver.getResource(possibleCFRefPath); if (adjustedResource != null) { if (adjustedResource.getParent() != null && adjustedResource.getParent().getName().equals("data")) { Resource contentRes = adjustedResource.getParent().getParent(); if (contentRes != null && contentRes.getName().equals(JcrConstants.JCR_CONTENT)) { Boolean isContentFragment = contentRes.getValueMap().get("contentFragment", Boolean.class); touchCFLastModified(resolver, contentRes, isContentFragment); } } } } /** * * @param resolver resolver to operate on. * @param contentRes Content Resource of adjusted resource * @param isContentFragment is Resource is ContentFragment Resource */ private void touchCFLastModified(ResourceResolver resolver, Resource contentRes, Boolean isContentFragment) { if (isContentFragment != null && isContentFragment) { ModifiableValueMap properties = contentRes.adaptTo(ModifiableValueMap.class); if (properties != null) { properties.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance()); properties.put(JcrConstants.JCR_LAST_MODIFIED_BY, resolver.getUserID()); // modify lastFragmentSave property (can be used for triggering a // workflow on save) properties.put("lastFragmentSave", Calendar.getInstance()); } else { log.warn("Cannot adapt " + contentRes.getPath() + " to ModifiableValueMap."); throw new IllegalStateException("properties should not be null, check logs for further investigation"); } } } /** * Adjusts all references to path
todestination
* in the properties below the specifiednode
. If {@link #isExact()} * istrue
only exact references topath
are * adjusted, otherwise all references to child resources are adjusted, too. * * @param node (content) node to traverse * @param path source path * @param destination destination path * @throws RepositoryException if an error during repository access occurs * @return collection of paths to properties that were adjusted */ public CollectionadjustReferences(Node node, String path, String destination) throws RepositoryException { return adjustReferences(node, path, destination, false, Collections. emptySet(), null, false); } public Collection adjustReferences(Node node, String path, String destination, boolean shallow, Set excludedProperties) throws RepositoryException { return adjustReferences(node, path, destination, shallow, excludedProperties, null, false); } public Collection adjustReferences(Node node, String path, String destination, boolean withoutChildNodes, ResourceResolver resolver) throws RepositoryException { return adjustReferences(node, path, destination, false, Collections. emptySet(), resolver, withoutChildNodes); } /** * Adjusts all references to path
todestination
* in the properties below the specifiednode
. If {@link #isExact()} * istrue
only exact references topath
are * adjusted, otherwise all references to child resources are adjusted, too. * * @param node (content) node to adjust * @param path source path * @param destination destination path * @param shallow iftrue
child nodes are not traversed * @param excludedProperties a set of excluded property names * @param resolver resolver to operate on. * @param withoutChildNodes with child nodes or not * @throws RepositoryException if an error during repository access occurs * @return collection of paths to properties that were adjusted */ public CollectionadjustReferences(Node node, String path, String destination, boolean shallow, Set excludedProperties, ResourceResolver resolver, boolean withoutChildNodes) throws RepositoryException { Set adjusted = new HashSet (); Pattern pattern = getReplacementPattern(path); String escDest = Text.escapePath(destination); for (PropertyIterator iter = node.getProperties(); iter.hasNext();) { Property p = iter.nextProperty(); // only check string, path and name properties if (!excludedProperties.contains(p.getName()) && p.getType() == PropertyType.STRING || p.getType() == PropertyType.NAME || p.getType() == PropertyType.PATH) { if (p.isMultiple()) { Value[] values = p.getValues(); boolean modified = false; for (int i=0; i null true
no child nodes are copied * @return rewritten path ornull
if not matches */ protected String rewrite(String value, String from, Pattern p, String to, String escTo, ResourceResolver resolver, boolean withoutChildNodes) { // first check unescaped direct property value if (value.equals(from)) { return to; } else if (value.startsWith(from + "#") || value.startsWith(from + ".html")) { // #34356 - handle cases where the path is followed by // an anchor or the .html suffix // TODO: There should be a less brittle way of doing this! return to + value.substring(from.length()); } else if (!exact) { if (value.startsWith(from + "/")) { return to + value.substring(from.length()); } } // ... then replace escaped references in rich text properties if (resolver != null && isValidatePage) { return replaceEscapedReferencesRTEWithValidHref(value, p, escTo, resolver, withoutChildNodes); } else { return replaceEscapedReferencesRTE(value, p, escTo); } } private static String replaceEscapedReferencesRTE(String value, Pattern p, String escTo) { Matcher m = p.matcher(value); StringBuffer ret = null; String repl = "$1" + escTo + "$3"; while (m.find()) { if (ret == null) { ret = new StringBuffer(); } m.appendReplacement(ret, repl); } if (ret == null) { return null; } else { m.appendTail(ret); return ret.toString(); } } private static String replaceEscapedReferencesRTEWithValidHref(String value, Pattern p, String escTo, ResourceResolver resolver, boolean withoutChildNodes) { Mapadjusted = new HashMap<>(); StringBuffer ret = null; Pattern pattern = Pattern.compile(HREF_REGEX); Matcher matcher = pattern.matcher(value); while (matcher.find()) { if (ret == null) { ret = new StringBuffer(); } String href = matcher.group(2); if (!adjusted.containsKey(href)) { updateAdjusted(p, escTo, resolver, withoutChildNodes, adjusted, href); } matcher.appendReplacement(ret, "$1" + adjusted.get(href)); } if (ret == null) { return null; } matcher.appendTail(ret); return ret.toString(); } private static void updateAdjusted(Pattern p, String escTo, ResourceResolver resolver, boolean withoutChildNodes, Map adjusted, String href) { String newHref = replaceEscapedReferencesRTE(href, p, escTo); if (newHref == null) { newHref = href; } if (href.equals(newHref)) { adjusted.put(href, href); return; } if (!withoutChildNodes) { adjusted.put(href, newHref); return; } Resource resource = resolver.resolve(newHref.replaceAll("^\"|\"$", "")); if (resource.getResourceType().equals(RESOURCE_TYPE_NON_EXISTING)) { adjusted.put(href, href); } else { adjusted.put(href, newHref); } } /** * Escapes illegal XPath search characters. * * @param s the string to encode * @return the escaped string */ public static String escapeIllegalXpathSearchChars(String s) { StringBuffer sb = new StringBuffer(); for (char c: s.toCharArray()) { if (c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '[' || c == ']' || c == '{' || c == '}' || c == '?' || c == '"' || c == '\\' || c == ' ' || c == '~') { sb.append('\\'); } else if (c == '\'') { sb.append(c); } sb.append(c); } return sb.toString(); } public ReferenceSearch setValidatePage(boolean validatePage) { isValidatePage = validatePage; return this; } /** * Holds information about the search results */ public static final class Info { private final Page page; private final String pageTitle; private final String pagePath; private final Set properties = new HashSet (); public Info(Page page) { this.page = page; pageTitle = page.getTitle(); pagePath = page.getPath(); } public Info(Page page, boolean hollow) { if (!hollow) { this.page = page; } else { this.page = null; } pageTitle = page.getTitle(); pagePath = page.getPath(); } private void addProperty(String path) { properties.add(path); } public Page getPage() { return page; } public Set getProperties() { return properties; } public String getPageTitle() { return pageTitle; } public String getPagePath() { return pagePath; } } }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy