All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.calcite.util.Template Maven / Gradle / Ivy

There is a newer version: 1.17.0-flink-r3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.calcite.util;

import com.google.common.collect.ImmutableList;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * String template.
 *
 * 

It is extended from {@link java.text.MessageFormat} to allow parameters * to be substituted by name as well as by position. * *

The following example, using MessageFormat, yields "Happy 64th birthday, * Ringo!": * *

MessageFormat f = * new MessageFormat("Happy {0,number}th birthday, {1}!");
* Object[] args = {64, "Ringo"};
* System.out.println(f.format(args);
* *

Here is the same example using a Template and named parameters: * *

Template f = * new Template("Happy {age,number}th birthday, {name}!");
* Map<Object, Object> args = new HashMap<Object, Object>();
* args.put("age", 64);
* args.put("name", "Ringo");
* System.out.println(f.format(args);
* *

Using a Template you can also use positional parameters: * *

Template f = * new Template("Happy {age,number}th birthday, {name}!");
* Object[] args = {64, "Ringo"};
* System.out.println(f.format(args);
* *

Or a hybrid; here, one argument is specified by name, another by position: * *

Template f = * new Template("Happy {age,number}th birthday, {name}!");
* Map<Object, Object> args = new HashMap<Object, Object>();
* args.put(0, 64);
* args.put("name", "Ringo");
* System.out.println(f.format(args);
*/ public class Template extends MessageFormat { private final List parameterNames; /** * Creates a Template for the default locale and the * specified pattern. * * @param pattern the pattern for this message format * @throws IllegalArgumentException if the pattern is invalid */ public static Template of(String pattern) { return of(pattern, Locale.getDefault()); } /** * Creates a Template for the specified locale and * pattern. * * @param pattern the pattern for this message format * @param locale the locale for this message format * @throws IllegalArgumentException if the pattern is invalid */ public static Template of(String pattern, Locale locale) { final List parameterNames = new ArrayList<>(); final String processedPattern = process(pattern, parameterNames); return new Template(processedPattern, parameterNames, locale); } private Template( String pattern, List parameterNames, Locale locale) { super(pattern, locale); this.parameterNames = ImmutableList.copyOf(parameterNames); } /** * Parses the pattern, populates the parameter names, and returns the * pattern with parameter names converted to parameter ordinals. * *

To ensure that the same parsing rules apply, this code is copied from * {@link java.text.MessageFormat#applyPattern(String)} but with different * actions when a parameter is recognized. * * @param pattern Pattern * @param parameterNames Names of parameters (output) * @return Pattern with named parameters substituted with ordinals */ private static String process(String pattern, List parameterNames) { StringBuilder[] segments = new StringBuilder[4]; for (int i = 0; i < segments.length; ++i) { segments[i] = new StringBuilder(); } int part = 0; boolean inQuote = false; int braceStack = 0; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (part == 0) { if (ch == '\'') { segments[part].append(ch); // jhyde: don't lose orig quote if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'') { segments[part].append(ch); // handle doubles ++i; } else { inQuote = !inQuote; } } else if (ch == '{' && !inQuote) { part = 1; } else { segments[part].append(ch); } } else if (inQuote) { // just copy quotes in parts segments[part].append(ch); if (ch == '\'') { inQuote = false; } } else { switch (ch) { case ',': if (part < 3) { part += 1; } else { segments[part].append(ch); } break; case '{': ++braceStack; segments[part].append(ch); break; case '}': if (braceStack == 0) { part = 0; makeFormat(segments, parameterNames); } else { --braceStack; segments[part].append(ch); } break; case '\'': inQuote = true; // fall through, so we keep quotes in other parts default: segments[part].append(ch); break; } } } if (braceStack == 0 && part != 0) { throw new IllegalArgumentException( "Unmatched braces in the pattern."); } return segments[0].toString(); } /** * Called when a complete parameter has been seen. * * @param segments Comma-separated segments of the parameter definition * @param parameterNames List of parameter names seen so far */ private static void makeFormat( StringBuilder[] segments, List parameterNames) { final String parameterName = segments[1].toString(); final int parameterOrdinal = parameterNames.size(); parameterNames.add(parameterName); segments[0].append("{"); segments[0].append(parameterOrdinal); final String two = segments[2].toString(); if (two.length() > 0) { segments[0].append(",").append(two); } final String three = segments[3].toString(); if (three.length() > 0) { segments[0].append(",").append(three); } segments[0].append("}"); segments[1].setLength(0); // throw away other segments segments[2].setLength(0); segments[3].setLength(0); } /** * Formats a set of arguments to produce a string. * *

Arguments may appear in the map using named keys (of type String), or * positional keys (0-based ordinals represented either as type String or * Integer). * * @param argMap A map containing the arguments as (key, value) pairs * @return Formatted string. * @throws IllegalArgumentException if the Format cannot format the given * object */ public String format(Map argMap) { Object[] args = new Object[parameterNames.size()]; for (int i = 0; i < parameterNames.size(); i++) { args[i] = getArg(argMap, i); } return format(args); } /** * Returns the value of the {@code ordinal}th argument. * * @param argMap Map of argument values * @param ordinal Ordinal of argument * @return Value of argument */ private Object getArg(Map argMap, int ordinal) { // First get by name. String parameterName = parameterNames.get(ordinal); Object arg = argMap.get(parameterName); if (arg != null) { return arg; } // Next by integer ordinal. arg = argMap.get(ordinal); if (arg != null) { return arg; } // Next by string ordinal. return argMap.get(ordinal + ""); } /** * Creates a Template with the given pattern and uses it * to format the given arguments. This is equivalent to *

* {@link #of(String) Template}(pattern).{@link #format}(args) *
* * @throws IllegalArgumentException if the pattern is invalid, * or if an argument in the * arguments array is not of the * type expected by the format element(s) * that use it. */ public static String formatByName( String pattern, Map argMap) { return Template.of(pattern).format(argMap); } /** * Returns the names of the parameters, in the order that they appeared in * the template string. * * @return List of parameter names */ public List getParameterNames() { return parameterNames; } } // End Template.java




© 2015 - 2024 Weber Informatics LLC | Privacy Policy