blob: 0f37db778d470b98cdf578f3ba8677052ad9144c [file] [log] [blame]
// Copyright 2006, 2007 The Apache Software Foundation
//
// Licensed 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.tapestry5.ioc.internal.services;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newLinkedList;
import org.apache.tapestry5.ioc.services.SymbolProvider;
import org.apache.tapestry5.ioc.services.SymbolSource;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class SymbolSourceImpl implements SymbolSource
{
private final List<SymbolProvider> providers;
/**
* Cache of symbol name to fully expanded symbol value.
*/
private final Map<String, String> cache = CollectionFactory.newConcurrentMap();
/**
* Contains execution data needed when performing an expansion (largely, to check for endless recursion).
*/
private class SymbolExpansion
{
private final LinkedList<String> expandingSymbols = newLinkedList();
String expandSymbols(String input)
{
StringBuilder builder = null;
int startx = 0;
while (true)
{
int symbolx = input.indexOf("${", startx);
// Special case: if the string contains no symbols then return it as is.
if (startx == 0 && symbolx < 0) return input;
// The string has at least one symbol, so its OK to create the StringBuilder
if (builder == null) builder = new StringBuilder();
// No more symbols found, so add in the rest of the string.
if (symbolx < 0)
{
builder.append(input.substring(startx));
break;
}
builder.append(input.substring(startx, symbolx));
int endx = input.indexOf("}", symbolx);
if (endx < 0)
{
String message = expandingSymbols.isEmpty() ? ServiceMessages
.missingSymbolCloseBrace(input) : ServiceMessages
.missingSymbolCloseBraceInPath(input, path());
throw new RuntimeException(message);
}
String symbolName = input.substring(symbolx + 2, endx);
builder.append(valueForSymbol(symbolName));
// Restart the search after the '}'
startx = endx + 1;
}
return builder.toString();
}
String valueForSymbol(String symbolName)
{
String value = cache.get(symbolName);
if (value == null)
{
value = expandSymbol(symbolName);
cache.put(symbolName, value);
}
return value;
}
String expandSymbol(String symbolName)
{
if (expandingSymbols.contains(symbolName))
{
expandingSymbols.add(symbolName);
throw new RuntimeException(ServiceMessages.recursiveSymbol(
symbolName,
pathFrom(symbolName)));
}
expandingSymbols.addLast(symbolName);
String value = null;
for (SymbolProvider provider : providers)
{
value = provider.valueForSymbol(symbolName);
if (value != null) break;
}
if (value == null)
{
String message = expandingSymbols.size() == 1 ? ServiceMessages
.symbolUndefined(symbolName) : ServiceMessages.symbolUndefinedInPath(
symbolName,
path());
throw new RuntimeException(message);
}
// The value may have symbols that need expansion.
String result = expandSymbols(value);
// And we're done expanding this symbol
expandingSymbols.removeLast();
return result;
}
String path()
{
StringBuilder builder = new StringBuilder();
boolean first = true;
for (String symbolName : expandingSymbols)
{
if (!first) builder.append(" --> ");
builder.append(symbolName);
first = false;
}
return builder.toString();
}
String pathFrom(String startSymbolName)
{
StringBuilder builder = new StringBuilder();
boolean first = true;
boolean match = false;
for (String symbolName : expandingSymbols)
{
if (!match)
{
if (symbolName.equals(startSymbolName))
match = true;
else
continue;
}
if (!first) builder.append(" --> ");
builder.append(symbolName);
first = false;
}
return builder.toString();
}
}
public SymbolSourceImpl(final List<SymbolProvider> providers)
{
this.providers = providers;
}
public String expandSymbols(String input)
{
return new SymbolExpansion().expandSymbols(input);
}
public String valueForSymbol(String symbolName)
{
String value = cache.get(symbolName);
// If already in the cache, then return it. Otherwise, let the SE find the value and
// update the cache.
return value != null ? value : new SymbolExpansion().valueForSymbol(symbolName);
}
}