blob: 568b6a1920d3fbc445c1e6e039097f663ca8d955 [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc.
*
* 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 com.google.clearsilver.jsilver.data;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Implementation of Data that allows for multiple underlying Data objects and checks each one in
* order for a value before giving up. Behaves like local HDF and global HDF in the JNI
* implementation of Clearsilver. This is only meant to be a root Data object and hardcodes that
* fact.
* <p>
* Note: If you have elements foo.1, foo.2, foo.3 in first Data object and foo.4, foo.5, foo.6 in
* second Data object, then fetching children of foo will return only foo.1 foo.2 foo.3 from first
* Data object.
*/
public class ChainedData extends DelegatedData {
public static final Logger logger = Logger.getLogger(ChainedData.class.getName());
// This mode allows developers to locate occurrences where they set the same HDF
// variable in multiple Data objects in the chain, which usually indicates
// bad planning or misuse.
public static final boolean DEBUG_MULTIPLE_ASSIGNMENTS = false;
Data[] dataList;
/**
* Optmization for case of single item.
*
* @param data a single data object to wrap.
*/
public ChainedData(Data data) {
super(data);
this.dataList = new Data[] {data};
}
public ChainedData(Data... dataList) {
super(getFirstData(dataList));
this.dataList = dataList;
}
public ChainedData(List<Data> dataList) {
super(getFirstData(dataList));
this.dataList = dataList.toArray(new Data[dataList.size()]);
}
@Override
protected DelegatedData newInstance(Data newDelegate) {
return newDelegate == null ? null : new ChainedData(newDelegate);
}
private static Data getFirstData(Data[] dataList) {
if (dataList.length == 0) {
throw new IllegalArgumentException("Must pass in at least one Data object to ChainedData.");
}
Data first = dataList[0];
if (first == null) {
throw new IllegalArgumentException("ChainedData does not accept null Data objects.");
}
return first;
}
private static Data getFirstData(List<Data> dataList) {
if (dataList.size() == 0) {
throw new IllegalArgumentException("Must pass in at least one Data object to ChainedData.");
}
Data first = dataList.get(0);
if (first == null) {
throw new IllegalArgumentException("ChainedData does not accept null Data objects.");
}
return first;
}
@Override
public Data getChild(String path) {
ArrayList<Data> children = null;
Data first = null;
for (Data d : dataList) {
Data child = d.getChild(path);
if (child != null) {
if (!DEBUG_MULTIPLE_ASSIGNMENTS) {
// If not in debug mode just return the first match. This assumes we are using the new
// style of VariableLocator that does not iteratively ask for each HDF path element
// separately.
return child;
}
if (first == null) {
// First match found
first = child;
} else if (children == null) {
// Second match found
children = new ArrayList<Data>(dataList.length);
children.add(first);
children.add(child);
} else {
// Third or more match found
children.add(child);
}
}
}
if (children == null) {
// 0 or 1 matches. Return first which is null or Data.
return first;
} else {
// Multiple matches. Pass back the first item found. This is only hit when
// DEBUG_MULTIPLE_ASSIGNMENTS is true.
logger.info("Found " + children.size() + " matches for path " + path);
return first;
}
}
@Override
public Data createChild(String path) {
Data child = getChild(path);
if (child != null) {
return child;
} else {
// We don't call super because we don't want to wrap the result in DelegatedData.
return dataList[0].createChild(path);
}
}
@Override
public String getValue(String path, String defaultValue) {
Data child = getChild(path);
if (child != null && child.getValue() != null) {
return child.getValue();
} else {
return defaultValue;
}
}
@Override
public int getIntValue(String path, int defaultValue) {
Data child = getChild(path);
if (child != null) {
String value = child.getValue();
try {
return value == null ? defaultValue : TypeConverter.parseNumber(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} else {
return defaultValue;
}
}
@Override
public String getValue(String path) {
Data child = getChild(path);
if (child != null) {
return child.getValue();
} else {
return null;
}
}
@Override
public int getIntValue(String path) {
Data child = getChild(path);
if (child != null) {
return child.getIntValue();
} else {
return 0;
}
}
@Override
public boolean getBooleanValue(String path) {
Data child = getChild(path);
if (child != null) {
return child.getBooleanValue();
} else {
return false;
}
}
@Override
public void toString(StringBuilder out, int indent) {
for (Data d : dataList) {
d.toString(out, indent);
}
}
@Override
public void write(Appendable out, int indent) throws IOException {
for (Data d : dataList) {
d.write(out, indent);
}
}
@Override
public void optimize() {
for (Data d : dataList) {
d.optimize();
}
}
}