Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.clearsilver.jsilver.data;
     18 
     19 import java.io.IOException;
     20 import java.util.ArrayList;
     21 import java.util.List;
     22 import java.util.logging.Logger;
     23 
     24 /**
     25  * Implementation of Data that allows for multiple underlying Data objects and checks each one in
     26  * order for a value before giving up. Behaves like local HDF and global HDF in the JNI
     27  * implementation of Clearsilver. This is only meant to be a root Data object and hardcodes that
     28  * fact.
     29  * <p>
     30  * Note: If you have elements foo.1, foo.2, foo.3 in first Data object and foo.4, foo.5, foo.6 in
     31  * second Data object, then fetching children of foo will return only foo.1 foo.2 foo.3 from first
     32  * Data object.
     33  */
     34 public class ChainedData extends DelegatedData {
     35   public static final Logger logger = Logger.getLogger(ChainedData.class.getName());
     36 
     37   // This mode allows developers to locate occurrences where they set the same HDF
     38   // variable in multiple Data objects in the chain, which usually indicates
     39   // bad planning or misuse.
     40   public static final boolean DEBUG_MULTIPLE_ASSIGNMENTS = false;
     41 
     42   Data[] dataList;
     43 
     44   /**
     45    * Optmization for case of single item.
     46    *
     47    * @param data a single data object to wrap.
     48    */
     49   public ChainedData(Data data) {
     50     super(data);
     51     this.dataList = new Data[] {data};
     52   }
     53 
     54   public ChainedData(Data... dataList) {
     55     super(getFirstData(dataList));
     56     this.dataList = dataList;
     57   }
     58 
     59   public ChainedData(List<Data> dataList) {
     60     super(getFirstData(dataList));
     61     this.dataList = dataList.toArray(new Data[dataList.size()]);
     62   }
     63 
     64   @Override
     65   protected DelegatedData newInstance(Data newDelegate) {
     66     return newDelegate == null ? null : new ChainedData(newDelegate);
     67   }
     68 
     69   private static Data getFirstData(Data[] dataList) {
     70     if (dataList.length == 0) {
     71       throw new IllegalArgumentException("Must pass in at least one Data object to ChainedData.");
     72     }
     73     Data first = dataList[0];
     74     if (first == null) {
     75       throw new IllegalArgumentException("ChainedData does not accept null Data objects.");
     76     }
     77     return first;
     78   }
     79 
     80   private static Data getFirstData(List<Data> dataList) {
     81     if (dataList.size() == 0) {
     82       throw new IllegalArgumentException("Must pass in at least one Data object to ChainedData.");
     83     }
     84     Data first = dataList.get(0);
     85     if (first == null) {
     86       throw new IllegalArgumentException("ChainedData does not accept null Data objects.");
     87     }
     88     return first;
     89   }
     90 
     91   @Override
     92   public Data getChild(String path) {
     93     ArrayList<Data> children = null;
     94     Data first = null;
     95     for (Data d : dataList) {
     96       Data child = d.getChild(path);
     97       if (child != null) {
     98         if (!DEBUG_MULTIPLE_ASSIGNMENTS) {
     99           // If not in debug mode just return the first match. This assumes we are using the new
    100           // style of VariableLocator that does not iteratively ask for each HDF path element
    101           // separately.
    102           return child;
    103         }
    104         if (first == null) {
    105           // First match found
    106           first = child;
    107         } else if (children == null) {
    108           // Second match found
    109           children = new ArrayList<Data>(dataList.length);
    110           children.add(first);
    111           children.add(child);
    112         } else {
    113           // Third or more match found
    114           children.add(child);
    115         }
    116       }
    117     }
    118     if (children == null) {
    119       // 0 or 1 matches. Return first which is null or Data.
    120       return first;
    121     } else {
    122       // Multiple matches. Pass back the first item found. This is only hit when
    123       // DEBUG_MULTIPLE_ASSIGNMENTS is true.
    124       logger.info("Found " + children.size() + " matches for path " + path);
    125       return first;
    126     }
    127   }
    128 
    129   @Override
    130   public Data createChild(String path) {
    131     Data child = getChild(path);
    132     if (child != null) {
    133       return child;
    134     } else {
    135       // We don't call super because we don't want to wrap the result in DelegatedData.
    136       return dataList[0].createChild(path);
    137     }
    138   }
    139 
    140   @Override
    141   public String getValue(String path, String defaultValue) {
    142     Data child = getChild(path);
    143     if (child != null && child.getValue() != null) {
    144       return child.getValue();
    145     } else {
    146       return defaultValue;
    147     }
    148   }
    149 
    150   @Override
    151   public int getIntValue(String path, int defaultValue) {
    152     Data child = getChild(path);
    153     if (child != null) {
    154       String value = child.getValue();
    155       try {
    156         return value == null ? defaultValue : TypeConverter.parseNumber(value);
    157       } catch (NumberFormatException e) {
    158         return defaultValue;
    159       }
    160     } else {
    161       return defaultValue;
    162     }
    163   }
    164 
    165   @Override
    166   public String getValue(String path) {
    167     Data child = getChild(path);
    168     if (child != null) {
    169       return child.getValue();
    170     } else {
    171       return null;
    172     }
    173   }
    174 
    175   @Override
    176   public int getIntValue(String path) {
    177     Data child = getChild(path);
    178     if (child != null) {
    179       return child.getIntValue();
    180     } else {
    181       return 0;
    182     }
    183   }
    184 
    185   @Override
    186   public boolean getBooleanValue(String path) {
    187     Data child = getChild(path);
    188     if (child != null) {
    189       return child.getBooleanValue();
    190     } else {
    191       return false;
    192     }
    193   }
    194 
    195   @Override
    196   public void toString(StringBuilder out, int indent) {
    197     for (Data d : dataList) {
    198       d.toString(out, indent);
    199     }
    200   }
    201 
    202   @Override
    203   public void write(Appendable out, int indent) throws IOException {
    204     for (Data d : dataList) {
    205       d.write(out, indent);
    206     }
    207   }
    208 
    209   @Override
    210   public void optimize() {
    211     for (Data d : dataList) {
    212       d.optimize();
    213     }
    214   }
    215 }
    216