001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.util; 020 021import java.util.List; 022import java.util.Map; 023import java.util.Map.Entry; 024 025import org.apache.yetus.audience.InterfaceAudience; 026 027import org.apache.hbase.thirdparty.com.google.common.escape.Escaper; 028import org.apache.hbase.thirdparty.com.google.common.escape.Escapers; 029 030/** 031 * Utility class for converting objects to JRuby. 032 * 033 * It handles null, Boolean, Number, String, byte[], List<Object>, Map<String, Object> 034 * structures. 035 * 036 * <p> 037 * E.g. 038 * <pre> 039 * Map<String, Object> map = new LinkedHashMap<>(); 040 * map.put("null", null); 041 * map.put("boolean", true); 042 * map.put("number", 1); 043 * map.put("string", "str"); 044 * map.put("binary", new byte[] { 1, 2, 3 }); 045 * map.put("list", Lists.newArrayList(1, "2", true)); 046 * </pre> 047 * </p> 048 * 049 * <p> 050 * Calling {@link #print(Object)} method will result: 051 * <pre> 052 * { null => '', boolean => 'true', number => '1', string => 'str', 053 * binary => '010203', list => [ '1', '2', 'true' ] } 054 * </pre> 055 * </p> 056 */ 057@InterfaceAudience.Private 058public final class JRubyFormat { 059 private static final Escaper escaper; 060 061 static { 062 escaper = Escapers.builder() 063 .addEscape('\\', "\\\\") 064 .addEscape('\'', "\\'") 065 .addEscape('\n', "\\n") 066 .addEscape('\r', "\\r") 067 .addEscape('\t', "\\t") 068 .addEscape('\f', "\\f") 069 .build(); 070 } 071 072 private JRubyFormat() { 073 } 074 075 private static String escape(Object object) { 076 if (object == null) { 077 return ""; 078 } else { 079 return escaper.escape(object.toString()); 080 } 081 } 082 083 @SuppressWarnings({ "unchecked" }) 084 private static void appendJRuby(StringBuilder builder, Object object) { 085 if (object == null) { 086 builder.append("''"); 087 } else if (object instanceof List) { 088 builder.append("["); 089 090 boolean first = true; 091 092 for (Object element: (List<Object>)object) { 093 if (first) { 094 first = false; 095 builder.append(" "); 096 } else { 097 builder.append(", "); 098 } 099 100 appendJRuby(builder, element); 101 } 102 103 if (!first) { 104 builder.append(" "); 105 } 106 107 builder.append("]"); 108 } else if (object instanceof Map) { 109 builder.append("{"); 110 111 boolean first = true; 112 113 for (Entry<String, Object> entry: ((Map<String, Object>)object).entrySet()) { 114 if (first) { 115 first = false; 116 builder.append(" "); 117 } else { 118 builder.append(", "); 119 } 120 121 String key = entry.getKey(); 122 String escapedKey = escape(key); 123 124 if (key.equals(escapedKey)) { 125 builder.append(key); 126 } else { 127 builder.append("'").append(escapedKey).append("'"); 128 } 129 130 builder.append(" => "); 131 appendJRuby(builder, entry.getValue()); 132 } 133 134 if (!first) { 135 builder.append(" "); 136 } 137 138 builder.append("}"); 139 } else if (object instanceof byte[]) { 140 String byteString = Bytes.toHex((byte[])object); 141 builder.append("'").append(escape(byteString)).append("'"); 142 } else { 143 builder.append("'").append(escape(object)).append("'"); 144 } 145 } 146 147 public static String print(Object object) { 148 StringBuilder builder = new StringBuilder(); 149 150 appendJRuby(builder, object); 151 152 return builder.toString(); 153 } 154}