/* * 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.myfaces.extensions.cdi.message.impl; import org.apache.myfaces.extensions.cdi.message.api.Message; import org.apache.myfaces.extensions.cdi.message.api.MessageContext; import org.apache.myfaces.extensions.cdi.message.api.MessageContextConfigAware; import org.apache.myfaces.extensions.cdi.message.api.MessageResolver; import org.apache.myfaces.extensions.cdi.message.api.MessageInterpolator; import org.apache.myfaces.extensions.cdi.message.api.NamedArgument; import org.apache.myfaces.extensions.cdi.message.api.Localizable; import org.apache.myfaces.extensions.cdi.message.api.MessageBuilder; import org.apache.myfaces.extensions.cdi.message.api.MessageFactory; import org.apache.myfaces.extensions.cdi.message.api.Formatter; import org.apache.myfaces.extensions.cdi.message.api.Default; import org.apache.myfaces.extensions.cdi.message.api.payload.MessagePayload; import org.apache.myfaces.extensions.cdi.message.api.payload.MessagePayloadKey; import org.apache.myfaces.extensions.cdi.message.api.payload.MessageSeverity; import org.apache.myfaces.extensions.cdi.message.api.payload.ArgumentDescriptor; import java.io.Serializable; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; /** * @author Gerhard Petracek */ class DefaultMessageBuilder implements MessageBuilder { private static final long serialVersionUID = 892218539314030675L; private List<Serializable> argumentList; private Set<NamedArgument> namedArguments; private MessageContext messageContext; private Map<Class, MessagePayload> messagePayload; private String messageDescriptor; private MessageFactory messageFactory; protected DefaultMessageBuilder() { } public DefaultMessageBuilder(MessageContext messageContext, MessageFactory messageFactory) { reset(); this.messageContext = new UnmodifiableMessageContext(messageContext.cloneContext()); if(messageFactory != null) { this.messageFactory = messageFactory; } else { this.messageFactory = new DefaultMessageFactory(); } } public MessageBuilder payload(MessagePayload... messagePayload) { Class key; for (MessagePayload payload : messagePayload) { key = payload.getClass(); if (payload.getClass().isAnnotationPresent(MessagePayloadKey.class)) { key = payload.getClass().getAnnotation(MessagePayloadKey.class).value(); } this.messagePayload.put(key, payload); } return this; } public MessageBuilder text(String messageDescriptor) { this.messageDescriptor = messageDescriptor; return this; } public MessageBuilder argument(Serializable... arguments) { for (Serializable argument : arguments) { if (argument instanceof NamedArgument) { this.namedArguments.add((NamedArgument) argument); } else { this.argumentList.add(argument); } } return this; } public MessageBuilder namedArgument(String name, Serializable value) { this.namedArguments.add(new DefaultNamedArgument(name, value)); return this; } public Message add() { Message result = create(); this.messageContext.addMessage(this.messageContext, result); return result; } private Message buildMessage() { if (this.messageDescriptor == null) { throw new IllegalStateException("messageDescriptor is missing"); } Message result = createNewMessage(); if(result instanceof MessageContextConfigAware && this.messageContext != null) { ((MessageContextConfigAware)result).setMessageContextConfig(this.messageContext.config()); } addArguments(result); addPayload(result); return result; } private Message createNewMessage() { if(this.messageFactory != null) { return this.messageFactory.create(this.messageDescriptor, getMessageSeverity()); } return new DefaultMessage(this.messageDescriptor, getMessageSeverity()); } private MessagePayload getMessageSeverity() { MessagePayload severity = this.messagePayload.get(MessageSeverity.class); if (severity == null) { severity = MessageSeverity.INFO; } return severity; } private void addArguments(Message result) { if (!this.argumentList.isEmpty()) { result.addArgument(this.argumentList.toArray(new Serializable[this.argumentList.size()])); } if (!this.namedArguments.isEmpty()) { result.addArgument(this.namedArguments.toArray(new Serializable[this.namedArguments.size()])); } } private void addPayload(Message result) { for (Map.Entry<Class, MessagePayload> entry : this.messagePayload.entrySet()) { if (!MessageSeverity.class.equals(entry.getKey())) { result.addPayload(entry.getKey(), entry.getValue()); } } } protected void reset() { this.messageDescriptor = null; this.messagePayload = new HashMap<Class, MessagePayload>(); this.argumentList = new ArrayList<Serializable>(); this.namedArguments = new HashSet<NamedArgument>(); } public Message create() { Message result = buildMessage(); reset(); return result; } public String toText() { Message baseMessage = buildMessage(); return getMessageText(baseMessage); } private String getMessageText(Message baseMessage) { String message = baseMessage.getDescriptor(); MessageResolver messageResolver = this.messageContext.config().getMessageResolver(); if (messageResolver != null) { message = resolveMessage(messageResolver, baseMessage); } MessageInterpolator messageInterpolator = this.messageContext.config().getMessageInterpolator(); if (messageInterpolator != null && message != null) { return checkedResult( interpolateMessage(messageInterpolator, message, restoreArguments(baseMessage, this.messageContext)), baseMessage); } return checkedResult(message, baseMessage); } private String checkedResult(String result, Message baseMessage) { if (result == null || isKey(baseMessage.getDescriptor()) || isKeyWithoutMarkers(result, baseMessage)) { String oldTemplate = extractTemplate(baseMessage.getDescriptor()); //minor performance tweak for inline-msg if (result == null || result.equals(oldTemplate)) { return MessageResolver.MISSING_RESOURCE_MARKER + oldTemplate + MessageResolver.MISSING_RESOURCE_MARKER + getArguments(baseMessage); } } return result; } private boolean isKeyWithoutMarkers(String result, Message baseMessage) { return (!result.contains(" ") && result.endsWith(baseMessage.getDescriptor())); } private String getArguments(Message message) { StringBuffer result = new StringBuffer(); Serializable argument; Serializable[] arguments = message.getArguments(); Formatter formatter; if(arguments == null || arguments.length == 0) { return ""; } for(int i = 0; i < arguments.length; i++) { if(i == 0) { result.append(" ("); } else { result.append(","); } argument = arguments[i]; formatter = this.messageContext.config().getFormatterFactory().findFormatter(argument.getClass()); if (formatter != null && !isDefaultFormatter(formatter.getClass())) { //noinspection unchecked result.append(formatter.format(this.messageContext, argument)); } else if(argument instanceof Localizable) { result.append(((Localizable)argument).toString(this.messageContext)); } else { //use default formatter (if available) if(formatter != null) { //noinspection unchecked result.append(formatter.format(this.messageContext, argument)); } else { result.append(argument.toString()); } } } result.append(')'); return result.toString(); } //see javadoc of {@link ArgumentDescriptor} for more details private Serializable[] restoreArguments(Message baseMessage, MessageContext messageContext) { if (messageContext == null || baseMessage.getArguments() == null) { return baseMessage.getArguments(); } List<Serializable> result = new ArrayList<Serializable>(baseMessage.getArguments().length); for (Serializable argument : baseMessage.getArguments()) { if (isNumberedArgumentValueALazyArgument(argument)) { resolveAndProcessLazyNumberedArgument(messageContext, result, (String)argument); } else if (argument instanceof NamedArgument && isNamedArgumentValueALazyArgument(((NamedArgument) argument))) { resolveAndProcessLazyNamedArgument(messageContext, result, (NamedArgument) argument); } else { result.add(argument); } } return result.toArray(new Serializable[result.size()]); } private boolean isNumberedArgumentValueALazyArgument(Serializable argument) { return argument instanceof String && isKey((String) argument); } private boolean isNamedArgumentValueALazyArgument(NamedArgument namedArgument) { return namedArgument.getValue() instanceof String && isKey((String) namedArgument.getValue()); } private void resolveAndProcessLazyNumberedArgument( MessageContext messageContext, List<Serializable> result, String argument) { String resolvedArgumentValue = resolveValueOfArgumentDescriptor(messageContext, argument); result.add(resolvedArgumentValue); } private void resolveAndProcessLazyNamedArgument( MessageContext messageContext, List<Serializable> result, NamedArgument argument) { String namedArgumentValue = (String) argument.getValue(); String resolvedNamedArgumentValue = resolveValueOfArgumentDescriptor(messageContext, namedArgumentValue); result.add(new DefaultNamedArgument(argument.getName(), resolvedNamedArgumentValue)); } private String resolveValueOfArgumentDescriptor(MessageContext messageContext, String argumentAsKey) { return messageContext.message() .text(argumentAsKey) .payload(ArgumentDescriptor.PAYLOAD) .toText(); } private boolean isDefaultFormatter(Class<? extends Formatter> formatterClass) { return formatterClass.isAnnotationPresent(Default.class); } private String extractTemplate(String template) { String result = getEscapedTemplate(template); if (isKey(result)) { result = extractTemplateKey(result); } return result; } private boolean isKey(String key) { return key.startsWith("{") && key.endsWith("}"); } private String extractTemplateKey(String key) { return key.substring(1, key.length() - 1); } private String resolveMessage(MessageResolver messageResolver, Message baseMessage) { return messageResolver.getMessage(this.messageContext, baseMessage.getDescriptor(), baseMessage.getPayload()); } private String interpolateMessage(MessageInterpolator messageInterpolator, String messageDescriptor, Serializable... arguments) { return messageInterpolator.interpolate(this.messageContext, getEscapedTemplate(messageDescriptor), arguments); } private String getEscapedTemplate(String messageDescriptor) { //TODO if (messageDescriptor.startsWith("\\{")) { return messageDescriptor.substring(1); } return messageDescriptor; } protected MessageContext getMessageContext() { return this.messageContext; } protected void setMessageFactory(MessageFactory messageFactory) { this.messageFactory = messageFactory; } }