1 /** 2 * Copyright (c) 2008, 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 package org.yaml.snakeyaml.resolver; 17 18 import java.util.ArrayList; 19 import java.util.HashMap; 20 import java.util.List; 21 import java.util.Map; 22 import java.util.regex.Pattern; 23 24 import org.yaml.snakeyaml.nodes.NodeId; 25 import org.yaml.snakeyaml.nodes.Tag; 26 27 /** 28 * Resolver tries to detect a type by content (when the tag is implicit) 29 */ 30 public class Resolver { 31 public static final Pattern BOOL = Pattern 32 .compile("^(?:yes|Yes|YES|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$"); 33 34 /** 35 * The regular expression is taken from the 1.2 specification but '_'s are 36 * added to keep backwards compatibility 37 */ 38 public static final Pattern FLOAT = Pattern 39 .compile("^([-+]?(\\.[0-9]+|[0-9_]+(\\.[0-9_]*)?)([eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"); 40 public static final Pattern INT = Pattern 41 .compile("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"); 42 public static final Pattern MERGE = Pattern.compile("^(?:<<)$"); 43 public static final Pattern NULL = Pattern.compile("^(?:~|null|Null|NULL| )$"); 44 public static final Pattern EMPTY = Pattern.compile("^$"); 45 public static final Pattern TIMESTAMP = Pattern 46 .compile("^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[ \t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](?:\\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$"); 47 public static final Pattern VALUE = Pattern.compile("^(?:=)$"); 48 public static final Pattern YAML = Pattern.compile("^(?:!|&|\\*)$"); 49 50 protected Map<Character, List<ResolverTuple>> yamlImplicitResolvers = new HashMap<Character, List<ResolverTuple>>(); 51 52 protected void addImplicitResolvers() { 53 addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO"); 54 /* 55 * INT must be before FLOAT because the regular expression for FLOAT 56 * matches INT (see issue 130) 57 * http://code.google.com/p/snakeyaml/issues/detail?id=130 58 */ 59 addImplicitResolver(Tag.INT, INT, "-+0123456789"); 60 addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789."); 61 addImplicitResolver(Tag.MERGE, MERGE, "<"); 62 addImplicitResolver(Tag.NULL, NULL, "~nN\0"); 63 addImplicitResolver(Tag.NULL, EMPTY, null); 64 addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789"); 65 // The following implicit resolver is only for documentation 66 // purposes. 67 // It cannot work 68 // because plain scalars cannot start with '!', '&', or '*'. 69 addImplicitResolver(Tag.YAML, YAML, "!&*"); 70 } 71 72 public Resolver() { 73 addImplicitResolvers(); 74 } 75 76 public void addImplicitResolver(Tag tag, Pattern regexp, String first) { 77 if (first == null) { 78 List<ResolverTuple> curr = yamlImplicitResolvers.get(null); 79 if (curr == null) { 80 curr = new ArrayList<ResolverTuple>(); 81 yamlImplicitResolvers.put(null, curr); 82 } 83 curr.add(new ResolverTuple(tag, regexp)); 84 } else { 85 char[] chrs = first.toCharArray(); 86 for (int i = 0, j = chrs.length; i < j; i++) { 87 Character theC = Character.valueOf(chrs[i]); 88 if (theC == 0) { 89 // special case: for null 90 theC = null; 91 } 92 List<ResolverTuple> curr = yamlImplicitResolvers.get(theC); 93 if (curr == null) { 94 curr = new ArrayList<ResolverTuple>(); 95 yamlImplicitResolvers.put(theC, curr); 96 } 97 curr.add(new ResolverTuple(tag, regexp)); 98 } 99 } 100 } 101 102 public Tag resolve(NodeId kind, String value, boolean implicit) { 103 if (kind == NodeId.scalar && implicit) { 104 List<ResolverTuple> resolvers = null; 105 if (value.length() == 0) { 106 resolvers = yamlImplicitResolvers.get('\0'); 107 } else { 108 resolvers = yamlImplicitResolvers.get(value.charAt(0)); 109 } 110 if (resolvers != null) { 111 for (ResolverTuple v : resolvers) { 112 Tag tag = v.getTag(); 113 Pattern regexp = v.getRegexp(); 114 if (regexp.matcher(value).matches()) { 115 return tag; 116 } 117 } 118 } 119 if (yamlImplicitResolvers.containsKey(null)) { 120 for (ResolverTuple v : yamlImplicitResolvers.get(null)) { 121 Tag tag = v.getTag(); 122 Pattern regexp = v.getRegexp(); 123 if (regexp.matcher(value).matches()) { 124 return tag; 125 } 126 } 127 } 128 } 129 switch (kind) { 130 case scalar: 131 return Tag.STR; 132 case sequence: 133 return Tag.SEQ; 134 default: 135 return Tag.MAP; 136 } 137 } 138 }