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 */ 018package org.apache.hadoop.hbase.util; 019 020import java.util.List; 021import java.util.Map; 022import java.util.Map.Entry; 023import org.apache.yetus.audience.InterfaceAudience; 024 025import org.apache.hbase.thirdparty.com.google.common.escape.Escaper; 026import org.apache.hbase.thirdparty.com.google.common.escape.Escapers; 027 028/** 029 * Utility class for converting objects to JRuby. It handles null, Boolean, Number, String, byte[], 030 * List<Object>, Map<String, Object> structures. 031 * <p> 032 * E.g. 033 * 034 * <pre> 035 * Map<String, Object> map = new LinkedHashMap<>(); 036 * map.put("null", null); 037 * map.put("boolean", true); 038 * map.put("number", 1); 039 * map.put("string", "str"); 040 * map.put("binary", new byte[] { 1, 2, 3 }); 041 * map.put("list", Lists.newArrayList(1, "2", true)); 042 * </pre> 043 * </p> 044 * <p> 045 * Calling {@link #print(Object)} method will result: 046 * 047 * <pre> 048 * { null => '', boolean => 'true', number => '1', string => 'str', 049 * binary => '010203', list => [ '1', '2', 'true' ] } 050 * </pre> 051 * </p> 052 */ 053@InterfaceAudience.Private 054public final class JRubyFormat { 055 private static final Escaper escaper; 056 057 static { 058 escaper = 059 Escapers.builder().addEscape('\\', "\\\\").addEscape('\'', "\\'").addEscape('\n', "\\n") 060 .addEscape('\r', "\\r").addEscape('\t', "\\t").addEscape('\f', "\\f").build(); 061 } 062 063 private JRubyFormat() { 064 } 065 066 private static String escape(Object object) { 067 if (object == null) { 068 return ""; 069 } else { 070 return escaper.escape(object.toString()); 071 } 072 } 073 074 @SuppressWarnings({ "unchecked" }) 075 private static void appendJRuby(StringBuilder builder, Object object) { 076 if (object == null) { 077 builder.append("''"); 078 } else if (object instanceof List) { 079 builder.append("["); 080 081 boolean first = true; 082 083 for (Object element : (List<Object>) object) { 084 if (first) { 085 first = false; 086 builder.append(" "); 087 } else { 088 builder.append(", "); 089 } 090 091 appendJRuby(builder, element); 092 } 093 094 if (!first) { 095 builder.append(" "); 096 } 097 098 builder.append("]"); 099 } else if (object instanceof Map) { 100 builder.append("{"); 101 102 boolean first = true; 103 104 for (Entry<String, Object> entry : ((Map<String, Object>) object).entrySet()) { 105 if (first) { 106 first = false; 107 builder.append(" "); 108 } else { 109 builder.append(", "); 110 } 111 112 String key = entry.getKey(); 113 String escapedKey = escape(key); 114 115 if (key.equals(escapedKey)) { 116 builder.append(key); 117 } else { 118 builder.append("'").append(escapedKey).append("'"); 119 } 120 121 builder.append(" => "); 122 appendJRuby(builder, entry.getValue()); 123 } 124 125 if (!first) { 126 builder.append(" "); 127 } 128 129 builder.append("}"); 130 } else if (object instanceof byte[]) { 131 String byteString = Bytes.toHex((byte[]) object); 132 builder.append("'").append(escape(byteString)).append("'"); 133 } else { 134 builder.append("'").append(escape(object)).append("'"); 135 } 136 } 137 138 public static String print(Object object) { 139 StringBuilder builder = new StringBuilder(); 140 141 appendJRuby(builder, object); 142 143 return builder.toString(); 144 } 145}