blob: 836a3cf417baf5571592cae2692ee9aa73d561b2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.resourceresolver.impl.mapping;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Designate(ocd = PlaceholderProviderConfiguration.class)
@Component(name = "org.apache.sling.resourceresolver.impl.mapping.PlaceholderProvider")
public class PlaceholderProviderImpl
implements PlaceholderProvider
{
/** Logger. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static PlaceholderProviderConfiguration DEFAULT_CONFIG;
static {
final InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(final Object obj, final Method calledMethod, final Object[] args)
throws Throwable {
if ( calledMethod.getDeclaringClass().isAssignableFrom(PlaceholderProviderConfiguration.class) ) {
return calledMethod.getDefaultValue();
}
if ( calledMethod.getDeclaringClass() == Object.class ) {
if ( calledMethod.getName().equals("toString") && (args == null || args.length == 0) ) {
return "Generated @" + PlaceholderProviderConfiguration.class.getName() + " instance";
}
if ( calledMethod.getName().equals("hashCode") && (args == null || args.length == 0) ) {
return this.hashCode();
}
if ( calledMethod.getName().equals("equals") && args != null && args.length == 1 ) {
return Boolean.FALSE;
}
}
throw new InternalError("unexpected method dispatched: " + calledMethod);
}
};
DEFAULT_CONFIG = (PlaceholderProviderConfiguration) Proxy.newProxyInstance(
PlaceholderProviderConfiguration.class.getClassLoader(),
new Class[] { PlaceholderProviderConfiguration.class },
handler
);
}
private BundleContext bundleContext;
private PlaceholderProviderConfiguration config = DEFAULT_CONFIG;
private Map<String, String> placeholderEntries = new HashMap<>();
// ---------- SCR Integration ---------------------------------------------
/**
* Activates this component (called by SCR before)
*/
@Activate
protected void activate(final BundleContext bundleContext, final PlaceholderProviderConfiguration config) {
this.bundleContext = bundleContext;
this.config = config;
for(String line: this.config.place_holder_key_value_pairs()) {
// Ignore no or empty lines
if(line == null || line.isEmpty()) { continue; }
// Ignore comments
if(line.charAt(0) == '#') { continue; }
int index = line.indexOf('=');
if(index <= 0) {
logger.warn("Placeholder Entry does not contain a key: '{}' -> ignored", line);
continue;
}
if(index > line.length() - 2) {
logger.warn("Placeholder Entry does not contain a value: '{}' -> ignored", line);
continue;
}
placeholderEntries.put(line.substring(0, index), line.substring(index + 1));
}
}
/**
* Modifies this component (called by SCR to update this component)
*/
@Modified
protected void modified(final BundleContext bundleContext, final PlaceholderProviderConfiguration config) {
this.deactivate();
this.activate(bundleContext, config);
}
/**
* Deactivates this component (called by SCR to take out of service)
*/
@Deactivate
protected void deactivate() {
this.bundleContext = null;
this.config = DEFAULT_CONFIG;
}
@Override
public Check hasPlaceholder(String line) {
STATUS status = STATUS.none;
List<PlaceholderContext> placeholderContextList = parseLine(line);
for(PlaceholderContext placeholderContext: placeholderContextList) {
String name = placeholderContext.getName();
if(!placeholderEntries.containsKey(name)) {
logger.warn("Placeholder: '{}' not found in list of Placeholders: '{}'", name, placeholderEntries);
status = STATUS.unknown;
}
status = status == STATUS.none ? STATUS.found : status;
}
return new Check(status, line, placeholderContextList);
}
@Override
public String resolve(Check check) {
if(check.getStatus() == STATUS.unknown) {
logger.warn("Line: '{}' contains unknown placeholders -> ignored", check.getLine());
return check.getLine();
}
List<PlaceholderContext> placeholderContextList = check.getPlaceholderContextList();
String line = check.getLine();
String answer = "";
if(placeholderContextList.isEmpty()) {
answer = line;
} else {
// The carret is the position in the source line. It is used to copy regular text
int carret = 0;
for (PlaceholderContext context : check.getPlaceholderContextList()) {
int start = context.getStart();
if(start > carret) {
// There is text between the current position in the source and the next placeholder
// so copy this into the target line
String text = line.substring(carret, start);
answer += text;
carret += text.length();
}
int end = context.getEnd();
String name = context.getName();
String value = placeholderEntries.get(name);
// Add placeholder value into the target line
answer += value;
carret = carret + end - start + PLACEHOLDER_END_TOKEN.length();
}
if(carret < line.length() - 1) {
// There is some text left after the last placeholder so copy this to the target line
answer += line.substring(carret);
}
}
return answer;
}
private List<PlaceholderContext> parseLine(String line) {
List<PlaceholderContext> answer = new ArrayList<>();
int index = -2;
if(line != null && !line.isEmpty()) {
while(true) {
index = line.indexOf(PLACEHOLDER_START_TOKEN, index + 1);
if (index < 0) {
break;
}
int index2 = line.indexOf(PLACEHOLDER_END_TOKEN, index);
if(index2 < 0) {
logger.warn("Given Line: '{}' contains an unclosed placeholder -> ignored", line);
continue;
}
answer.add(
new PlaceholderContext(
index,
index2,
line.substring(index + PLACEHOLDER_START_TOKEN.length(), index2)
)
);
}
}
return answer;
}
}