/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.rendering.macro.box;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.FormatBlock;
import org.xwiki.rendering.block.GroupBlock;
import org.xwiki.rendering.block.ImageBlock;
import org.xwiki.rendering.block.NewLineBlock;
import org.xwiki.rendering.listener.Format;
import org.xwiki.rendering.listener.reference.ResourceReference;
import org.xwiki.rendering.listener.reference.ResourceType;
import org.xwiki.rendering.macro.AbstractMacro;
import org.xwiki.rendering.macro.MacroContentParser;
import org.xwiki.rendering.macro.MacroExecutionException;
import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
import org.xwiki.rendering.parser.ResourceReferenceParser;
import org.xwiki.rendering.transformation.MacroTransformationContext;
/**
* Draw a box around provided content.
*
* @param <P> the type of macro parameters bean.
* @version $Id: c1fcf6b776bd34880a3758e72a643e7b4eb89448 $
* @since 1.7
*/
public abstract class AbstractBoxMacro<P extends BoxMacroParameters> extends AbstractMacro<P>
{
/**
* Parses untyped image references.
*/
@Inject
@Named("image/untyped")
private ResourceReferenceParser untypedImageReferenceParser;
/**
* The parser used to parse box content and box title parameter.
*/
@Inject
private MacroContentParser contentParser;
/**
* Creates a new box macro.
*
* @param name the name of the macro
* @param description string describing this macro.
* @param contentDescriptor the {@link ContentDescriptor} describing the content of this macro.
* @param parametersBeanClass class of the parameters bean.
*/
protected AbstractBoxMacro(String name, String description, ContentDescriptor contentDescriptor,
Class<?> parametersBeanClass)
{
super(name, description, contentDescriptor, parametersBeanClass);
}
@Override
public boolean supportsInlineMode()
{
return true;
}
@Override
public List<Block> execute(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
// TODO: Refactor this when it'll possible to have a specific converter associated to a macro parameter.
ResourceReference imageReference = parameters.getImage();
// If the image reference is unknown then resolve it with the untyped resource reference parser
// (this happens when the user doesn't specify a type for the image reference).
if (imageReference != null && imageReference.getType().equals(ResourceType.UNKNOWN)) {
imageReference = this.untypedImageReferenceParser.parse(imageReference.getReference());
}
String titleParameter = parameters.getTitle();
List<? extends Block> titleBlockList = parameters.getBlockTitle();
// Use a linked hashmap to keep the parameters in the same order as we create them when they are retrieved
// by renderers. This is useful for example in the Event renderer to control the order in which the params
// are displayed.
Map<String, String> boxParameters = new LinkedHashMap<String, String>();
String classParameter = parameters.getCssClass();
String cssClass =
StringUtils.isEmpty(classParameter) ? getClassProperty() : getClassProperty() + " " + classParameter;
boxParameters.put("class", cssClass);
if (!StringUtils.isEmpty(parameters.getWidth())) {
boxParameters.put("style", "width:" + parameters.getWidth());
}
Block boxBlock;
if (content != null) {
if (context.isInline()) {
List<Block> contentBlocks = parseContent(parameters, content, context);
FormatBlock spanBlock = new FormatBlock(contentBlocks, Format.NONE);
spanBlock.setParameters(boxParameters);
boxBlock = spanBlock;
} else {
boxBlock = new GroupBlock(boxParameters);
// we add the image, if there is one
if (imageReference != null) {
Block imageBlock = new ImageBlock(imageReference, true);
boxBlock.addChild(imageBlock);
boxBlock.addChild(new NewLineBlock());
}
// we add the title, if there is one
if (!StringUtils.isEmpty(titleParameter)) {
// Don't execute transformations explicitly. They'll be executed on the generated content later on.
boxBlock.addChildren(this.contentParser.parse(titleParameter, context, false, true).getChildren());
}
if (titleBlockList != null) {
boxBlock.addChildren(titleBlockList);
}
List<Block> contentBlocks = parseContent(parameters, content, context);
boxBlock.addChildren(contentBlocks);
}
return Collections.singletonList(boxBlock);
} else {
return Collections.emptyList();
}
}
/**
* Execute macro content and return the result. This methods is separated form
* {@link #execute(BoxMacroParameters, String, MacroTransformationContext)} to be able to overwrite it in macro
* which need boxes.
*
* @param parameters the parameters of the macro.
* @param content the content of the macro.
* @param context the context if the macros transformation.
* @return the result of the macro execution.
* @throws MacroExecutionException error when executing the macro.
*/
protected abstract List<Block> parseContent(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException;
/**
* @return the name of the CSS class to use when rendering, in case no cssClass parameter is specified.
*/
protected String getClassProperty()
{
return "box";
}
/**
* @return the macro content parser to use to parse content in wiki syntax
*/
protected MacroContentParser getMacroContentParser()
{
return this.contentParser;
}
}