/*
*
* 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.flex.compiler.clients.problems;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import org.apache.flex.compiler.Messages;
import org.apache.flex.compiler.problems.CompilerProblem;
import org.apache.flex.compiler.problems.ICompilerProblem;
/**
* Abstract class that provide useful functionality for
* formatting and retrieving the localized message of {@link CompilerProblem}'s.
*/
public class ProblemFormatter
{
public static ProblemFormatter DEFAULT_FORMATTER = new ProblemFormatter();
/**
* Returns a readable description of the problem, by substituting field
* values for named placeholders such as ${name} in the message specified
* for this compiler problem.
*
* @param problem problem whose message to find
* @return A localized readable description of the problem.
*/
public String format(ICompilerProblem problem)
{
Class<?> c = problem.getClass();
String problemMessage = getMessage(c.getSimpleName());
if(problemMessage == null)
{
// Use reflection to look up the value of the static field
// named 'description' on the problem subclass.
// For example, if this problem is a SyntaxError, this will be
// "Syntax error: '${tokenText}' is not allowed here"
try
{
Field descriptionField = c.getDeclaredField("DESCRIPTION");
problemMessage = (String)descriptionField.get(null);
}
catch (Exception e)
{
//No message found for this problem so just returned
//the name of the class which will also be a bit helpful.
return '!' + c.getSimpleName() + '!';
}
}
// Use reflection on this class and its superclasses
// to build a name/value map from the public non-static fields.
final Map<String, Object> map = new HashMap<String, Object>();
while (c != Object.class)
{
for (final Field field : c.getDeclaredFields())
{
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers))
{
try
{
// Normally there should not be null values in a problem field, but
// It has happened once or twice... Better to not attempt the substitution,
// since it will just NPE and no problems we be correctly reported.
Object value = field.get(problem);
if (value != null)
map.put(field.getName(), value);
}
catch (Exception e)
{
// ignore
}
}
}
c = c.getSuperclass();
}
//Replace all the tokens in the description using this key/value map
return substitute(problemMessage, map);
}
/**
* Resolves the tokens in the text using the key/value pairs in the
* specified map. Each token in the message is searched in the map's key
* list and if found, the token replaced with the value of found key.
*
* @param text compiler message to resolve
* @param parameters map that contains key/value pairs used to resolve the
* tokens in the specified message.
* @return resolved, token-free compiler message
*/
public static String substitute(String text, final Map<String, Object> parameters)
{
for (String key : parameters.keySet())
{
String value = parameters.get(key).toString();
if(value != null)
{
// Need to escape the value in case it contains special characters, like '$'
value = Matcher.quoteReplacement(value);
text = text.replaceAll("[$][{]" + key + "[}]", value);
}
}
return text;
}
/**
* Returns message for the specified key using the resource
* bundle generated for the specified locale.
*
* @param key key to find (should be the class name of the problem class)
* @return the message associated with the specified key
*/
protected String getMessage(String key)
{
return Messages.getString(key);
}
}