View Javadoc

    1   /**
    2    * Copyright (c) 2008-2011, http://www.snakeyaml.org
    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 org.yaml.snakeyaml.representer;
    18
    19  import java.math.BigInteger;
    20  import java.util.Arrays;
    21  import java.util.Calendar;
    22  import java.util.Date;
    23  import java.util.HashMap;
    24  import java.util.Iterator;
    25  import java.util.LinkedHashMap;
    26  import java.util.List;
    27  import java.util.Map;
    28  import java.util.Set;
    29  import java.util.TimeZone;
    30  import java.util.regex.Pattern;
    31
    32  import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
    33  import org.yaml.snakeyaml.nodes.Node;
    34  import org.yaml.snakeyaml.nodes.Tag;
    35
    36  /**
    37   * Represent standard Java classes
    38   */
    39  class SafeRepresenter extends BaseRepresenter {
    40
    41      protected Map<Class<? extends Object>, Tag> classTags;
    42
    43      public SafeRepresenter() {
    44          this.nullRepresenter = new RepresentNull();
    45          this.representers.put(String.class, new RepresentString());
    46          this.representers.put(Boolean.class, new RepresentBoolean());
    47          this.representers.put(Character.class, new RepresentString());
    48          this.representers.put(byte[].class, new RepresentByteArray());
    49          this.multiRepresenters.put(Number.class, new RepresentNumber());
    50          this.multiRepresenters.put(List.class, new RepresentList());
    51          this.multiRepresenters.put(Map.class, new RepresentMap());
    52          this.multiRepresenters.put(Set.class, new RepresentSet());
    53          this.multiRepresenters.put(Iterator.class, new RepresentIterator());
    54          this.multiRepresenters.put(new Object[0].getClass(), new RepresentArray());
    55          this.multiRepresenters.put(Date.class, new RepresentDate());
    56          this.multiRepresenters.put(Enum.class, new RepresentEnum());
    57          this.multiRepresenters.put(Calendar.class, new RepresentDate());
    58          classTags = new HashMap<Class<? extends Object>, Tag>();
    59      }
    60
    61      protected Tag getTag(Class<?> clazz, Tag defaultTag) {
    62          if (classTags.containsKey(clazz)) {
    63              return classTags.get(clazz);
    64          } else {
    65              return defaultTag;
    66          }
    67      }
    68
    69      /**
    70       * Define a tag for the <code>Class</code> to serialize
    71       *
    72       * @deprecated use Tag instead of String
    73       * @param clazz
    74       *            <code>Class</code> which tag is changed
    75       * @param tag
    76       *            new tag to be used for every instance of the specified
    77       *            <code>Class</code>
    78       * @return the previous tag associated with the <code>Class</code>
    79       */
    80      public Tag addClassTag(Class<? extends Object> clazz, String tag) {
    81          return addClassTag(clazz, new Tag(tag));
    82      }
    83
    84      /**
    85       * Define a tag for the <code>Class</code> to serialize.
    86       *
    87       * @param clazz
    88       *            <code>Class</code> which tag is changed
    89       * @param tag
    90       *            new tag to be used for every instance of the specified
    91       *            <code>Class</code>
    92       * @return the previous tag associated with the <code>Class</code>
    93       */
    94      public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
    95          if (tag == null) {
    96              throw new NullPointerException("Tag must be provided.");
    97          }
    98          return classTags.put(clazz, tag);
    99      }
    100
    101     protected class RepresentNull implements Represent {
    102         public Node representData(Object data) {
    103             return representScalar(Tag.NULL, "null");
    104         }
    105     }
    106
    107     public static Pattern BINARY_PATTERN = Pattern.compile("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]");
    108
    109     protected class RepresentString implements Represent {
    110         public Node representData(Object data) {
    111             Tag tag = Tag.STR;
    112             Character style = null;
    113             String value = data.toString();
    114             if (BINARY_PATTERN.matcher(value).find()) {
    115                 tag = Tag.BINARY;
    116                 char[] binary;
    117                 binary = Base64Coder.encode(value.getBytes());
    118                 value = String.valueOf(binary);
    119                 style = '|';
    120             }
    121             return representScalar(tag, value, style);
    122         }
    123     }
    124
    125     protected class RepresentBoolean implements Represent {
    126         public Node representData(Object data) {
    127             String value;
    128             if (Boolean.TRUE.equals(data)) {
    129                 value = "true";
    130             } else {
    131                 value = "false";
    132             }
    133             return representScalar(Tag.BOOL, value);
    134         }
    135     }
    136
    137     protected class RepresentNumber implements Represent {
    138         public Node representData(Object data) {
    139             Tag tag;
    140             String value;
    141             if (data instanceof Byte || data instanceof Short || data instanceof Integer
    142                     || data instanceof Long || data instanceof BigInteger) {
    143                 tag = Tag.INT;
    144                 value = data.toString();
    145             } else {
    146                 Number number = (Number) data;
    147                 tag = Tag.FLOAT;
    148                 if (number.equals(Double.NaN)) {
    149                     value = ".NaN";
    150                 } else if (number.equals(Double.POSITIVE_INFINITY)) {
    151                     value = ".inf";
    152                 } else if (number.equals(Double.NEGATIVE_INFINITY)) {
    153                     value = "-.inf";
    154                 } else {
    155                     value = number.toString();
    156                 }
    157             }
    158             return representScalar(getTag(data.getClass(), tag), value);
    159         }
    160     }
    161
    162     protected class RepresentList implements Represent {
    163         @SuppressWarnings("unchecked")
    164         public Node representData(Object data) {
    165             return representSequence(getTag(data.getClass(), Tag.SEQ), (List<Object>) data, null);
    166         }
    167     }
    168
    169     protected class RepresentIterator implements Represent {
    170         @SuppressWarnings("unchecked")
    171         public Node representData(Object data) {
    172             Iterator<Object> iter = (Iterator<Object>) data;
    173             return representSequence(getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter),
    174                     null);
    175         }
    176     }
    177
    178     private class IteratorWrapper implements Iterable<Object> {
    179         private Iterator<Object> iter;
    180
    181         public IteratorWrapper(Iterator<Object> iter) {
    182             this.iter = iter;
    183         }
    184
    185         public Iterator<Object> iterator() {
    186             return iter;
    187         }
    188     }
    189
    190     protected class RepresentArray implements Represent {
    191         public Node representData(Object data) {
    192             Object[] array = (Object[]) data;
    193             List<Object> list = Arrays.asList(array);
    194             return representSequence(Tag.SEQ, list, null);
    195         }
    196     }
    197
    198     protected class RepresentMap implements Represent {
    199         @SuppressWarnings("unchecked")
    200         public Node representData(Object data) {
    201             return representMapping(getTag(data.getClass(), Tag.MAP), (Map<Object, Object>) data,
    202                     null);
    203         }
    204     }
    205
    206     protected class RepresentSet implements Represent {
    207         @SuppressWarnings("unchecked")
    208         public Node representData(Object data) {
    209             Map<Object, Object> value = new LinkedHashMap<Object, Object>();
    210             Set<Object> set = (Set<Object>) data;
    211             for (Object key : set) {
    212                 value.put(key, null);
    213             }
    214             return representMapping(getTag(data.getClass(), Tag.SET), value, null);
    215         }
    216     }
    217
    218     protected class RepresentDate implements Represent {
    219         public Node representData(Object data) {
    220             // because SimpleDateFormat ignores timezone we have to use Calendar
    221             Calendar calendar;
    222             if (data instanceof Calendar) {
    223                 calendar = (Calendar) data;
    224             } else {
    225                 calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    226                 calendar.setTime((Date) data);
    227             }
    228             int years = calendar.get(Calendar.YEAR);
    229             int months = calendar.get(Calendar.MONTH) + 1; // 0..12
    230             int days = calendar.get(Calendar.DAY_OF_MONTH); // 1..31
    231             int hour24 = calendar.get(Calendar.HOUR_OF_DAY); // 0..24
    232             int minutes = calendar.get(Calendar.MINUTE); // 0..59
    233             int seconds = calendar.get(Calendar.SECOND); // 0..59
    234             int millis = calendar.get(Calendar.MILLISECOND);
    235             StringBuilder buffer = new StringBuilder(String.valueOf(years));
    236             while (buffer.length() < 4) {
    237                 // ancient years
    238                 buffer.insert(0, "0");
    239             }
    240             buffer.append("-");
    241             if (months < 10) {
    242                 buffer.append("0");
    243             }
    244             buffer.append(String.valueOf(months));
    245             buffer.append("-");
    246             if (days < 10) {
    247                 buffer.append("0");
    248             }
    249             buffer.append(String.valueOf(days));
    250             buffer.append("T");
    251             if (hour24 < 10) {
    252                 buffer.append("0");
    253             }
    254             buffer.append(String.valueOf(hour24));
    255             buffer.append(":");
    256             if (minutes < 10) {
    257                 buffer.append("0");
    258             }
    259             buffer.append(String.valueOf(minutes));
    260             buffer.append(":");
    261             if (seconds < 10) {
    262                 buffer.append("0");
    263             }
    264             buffer.append(String.valueOf(seconds));
    265             if (millis > 0) {
    266                 if (millis < 10) {
    267                     buffer.append(".00");
    268                 } else if (millis < 100) {
    269                     buffer.append(".0");
    270                 } else {
    271                     buffer.append(".");
    272                 }
    273                 buffer.append(String.valueOf(millis));
    274             }
    275             if (TimeZone.getTimeZone("UTC").equals(calendar.getTimeZone())) {
    276                 buffer.append("Z");
    277             } else {
    278                 // Get the Offset from GMT taking DST into account
    279                 int gmtOffset = calendar.getTimeZone().getOffset(calendar.get(Calendar.ERA),
    280                         calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
    281                         calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.DAY_OF_WEEK),
    282                         calendar.get(Calendar.MILLISECOND));
    283                 int minutesOffset = gmtOffset / (60 * 1000);
    284                 int hoursOffset = minutesOffset / 60;
    285                 int partOfHour = minutesOffset % 60;
    286                 buffer.append((hoursOffset > 0 ? "+" : "") + hoursOffset + ":"
    287                         + (partOfHour < 10 ? "0" + partOfHour : partOfHour));
    288             }
    289             return representScalar(getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), null);
    290         }
    291     }
    292
    293     protected class RepresentEnum implements Represent {
    294         public Node representData(Object data) {
    295             Tag tag = new Tag(data.getClass());
    296             return representScalar(getTag(data.getClass(), tag), ((Enum<?>) data).name());
    297         }
    298     }
    299
    300     protected class RepresentByteArray implements Represent {
    301         public Node representData(Object data) {
    302             char[] binary = Base64Coder.encode((byte[]) data);
    303             return representScalar(Tag.BINARY, String.valueOf(binary), '|');
    304         }
    305     }
    306 }