/* * 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. *

* 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 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 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 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(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(); } } }