/* * Copyright 2015 Coursera Inc. * * Licensed 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 com.linkedin.data.schema.grammar; import com.linkedin.data.grammar.PdlParser; import java.math.BigDecimal; import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; /** * Utility methods for the Pdl.g4 antlr grammar. * * @author Joe Betz */ public class PdlParseUtils { /** * Given a doc string comment, unescapes and extracts the contents. * * In PDL, doc string are expected to be formatted using markdown. * * @param schemaDoc provides the doc comment to extract and unescape. * @return a markdown formatted string. */ public static String extractMarkdown(String schemaDoc) { String trimmed = schemaDoc.trim(); String withoutMargin = stripMargin(trimmed.substring(3, trimmed.length() - 2).trim()); return unescapeDocstring(withoutMargin.trim()); } /** * Unescapes the markdown contents of a doc string. * * @param escaped provides the escaped markdown contents of a doc string. * @return a markdown formatted string. */ private static String unescapeDocstring(String escaped) { // unescape "/*" and "*/" String commentUnescaped = escaped.replace("/*", "/*").replace("*/", "*/"); return StringEscapeUtils.unescapeHtml4(commentUnescaped); } /** * Unescapes a PDL string literal. * * @param stringLiteral provides an escaped PDL string literal. * @return a string literal. */ public static String extractString(String stringLiteral) { return StringEscapeUtils.unescapeJson(stringLiteral.substring(1, stringLiteral.length() - 1)); } /** * Strips the left margin, delimited by '*' from text within a PDL doc comment. * * Based on Scala's implementation of StringLike.stripMargin. * * @param schemadoc provides a PDL doc contents with the margin still present. * @return a doc comment with the margin removed. */ public static String stripMargin(String schemadoc) { char marginChar = '*'; StringBuilder buf = new StringBuilder(); for (String lineWithoutSeparator : schemadoc.split(System.lineSeparator())) { String line = lineWithoutSeparator + System.lineSeparator(); int len = line.length(); int index = 0; while (index < len && line.charAt(index) <= ' ') { index++; } if (index < len && line.charAt(index) == marginChar) { buf.append(line.substring(index + 1)); } else { buf.append(line); } } return buf.toString(); } /** * Unescape an escaped PDL identifier that has been escaped using `` to avoid collisions with * keywords. E.g. `namespace`. * * @param identifier provides the identifier to escape. * @return an identifier. */ public static String unescapeIdentifier(String identifier) { return identifier.replaceAll("`", ""); } /** * Concatenates identifiers into a '.' delimited string. * * @param identifiers provides the ordered list of identifiers to join. * @return a string. */ public static String join(List<PdlParser.IdentifierContext> identifiers) { StringBuilder stringBuilder = new StringBuilder(); Iterator<PdlParser.IdentifierContext> iter = identifiers.iterator(); while (iter.hasNext()) { stringBuilder.append(iter.next().value); if (iter.hasNext()) { stringBuilder.append("."); } } return stringBuilder.toString(); } /** * Deserializes a JSON number to a java Number. * @param string provide a string representation of a JSON number. * @return a Number. */ public static Number toNumber(String string) { BigDecimal bigDecimal = new BigDecimal(string); if (StringUtils.containsAny(string, '.', 'e', 'E')) { double d = bigDecimal.doubleValue(); if (Double.isFinite(d)) { return d; } else { return bigDecimal; } } else { long l = bigDecimal.longValueExact(); int i = (int) l; if (i == l) { return (int) l; } else { return l; } } } }