/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.properties.editor.util;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.springframework.ide.eclipse.editor.support.util.StringUtil;
/**
* Converts types in notation used by spring properties metadata into a 'Structured' form
*
* @author Kris De Volder
*/
public class TypeParser {
private static final String DELIM = "<>,";
/**
* Wrapper around StringTokenizer that manages a single lookahead token.
* So it can implement 'peekToken()' method.
*/
private static class Tokener {
private String lookahead;
private StringTokenizer tokens;
public Tokener(String input) {
this.tokens = new StringTokenizer(input, DELIM, true);
}
/**
* Fetch next token. Returns null if there are no more tokens.
*/
public String nextToken() {
if (lookahead!=null) {
try {
return lookahead;
} finally {
lookahead = null;
}
} else if (tokens.hasMoreTokens()) {
return tokens.nextToken();
}
return null;
}
/**
* Fetch the next token without consuming it.
* Returns null if there are no more tokens.
*/
public String peekToken() {
if (lookahead!=null) {
return lookahead;
} else if (tokens.hasMoreTokens()) {
lookahead = tokens.nextToken();
return lookahead;
}
return null;
}
}
private Tokener input;
private TypeParser(String input) {
this.input = new Tokener(input);
}
public static Type parse(String str) {
if (StringUtil.hasText(str)) {
return new TypeParser(str).parseType();
}
return null;
}
private Type parseType() {
String ident = input.nextToken();
String token = input.peekToken();
if ("<".equals(token)) {
ArrayList<Type> params = parseParams();
return new Type(ident, params.toArray(new Type[params.size()]));
} else {
return new Type(ident, null);
}
}
private ArrayList<Type> parseParams() {
skip("<");
try {
return parseParamList(new ArrayList<Type>());
} finally {
skip(">");
}
}
private ArrayList<Type> parseParamList(ArrayList<Type> params) {
//parse params separate by ",'
String tok = input.peekToken();
if (isIdent(tok)) {
params.add(parseType());
if (skip(",")) {
return parseParamList(params);
}
}
return params;
}
/**
* Skip an expected token, or do nothing if the next token is
* something unexpected.
* @return whether token was skipped.
*/
private boolean skip(String expected) {
String t = input.peekToken();
if (expected.equals(t)) {
input.nextToken();
return true;
}
return false;
}
public boolean isIdent(String token) {
return token!=null && !isSeparator(token);
}
private boolean isSeparator(String token) {
if (token!=null && token.length()==1) {
int len = DELIM.length();
char c = token.charAt(0);
for (int i = 0; i < len; i++) {
if (DELIM.charAt(i)==c) {
return true;
}
}
}
return false;
}
}