/* * 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.internal.macro.code; import java.io.StringReader; import java.util.Arrays; 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 javax.inject.Singleton; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; import org.xwiki.rendering.block.Block; import org.xwiki.rendering.block.FormatBlock; import org.xwiki.rendering.block.GroupBlock; import org.xwiki.rendering.listener.Format; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.box.AbstractBoxMacro; import org.xwiki.rendering.macro.code.CodeMacroParameters; import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; import org.xwiki.rendering.parser.HighlightParser; import org.xwiki.rendering.parser.ParseException; import org.xwiki.rendering.parser.Parser; import org.xwiki.rendering.transformation.MacroTransformationContext; /** * Highlight provided content depending of the content syntax. * * @version $Id: 41031c46c4ea35309d561f7bfb16602a6670bd1d $ * @since 1.7RC1 */ @Component @Named("code") @Singleton public class CodeMacro extends AbstractBoxMacro<CodeMacroParameters> { /** * The description of the macro. */ private static final String DESCRIPTION = "Highlights code snippets of various programming languages"; /** * Used to indicate that content should not be highlighted. */ private static final String LANGUAGE_NONE = "none"; /** * The description of the macro content. */ private static final String CONTENT_DESCRIPTION = "the content to highlight"; /** * Used to parse content when language="none". */ @Inject @Named("plain/1.0") private Parser plainTextParser; /** * Used to lookup highlight parsers. */ @Inject private ComponentManager componentManager; /** * The logger to log. */ @Inject private Logger logger; /** * Create and initialize the descriptor of the macro. */ public CodeMacro() { super("Code", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION), CodeMacroParameters.class); setDefaultCategory(DEFAULT_CATEGORY_FORMATTING); } @Override protected List<Block> parseContent(CodeMacroParameters parameters, String content, MacroTransformationContext context) throws MacroExecutionException { List<Block> result; try { if (LANGUAGE_NONE.equalsIgnoreCase(parameters.getLanguage())) { if (StringUtils.isEmpty(content)) { result = Collections.emptyList(); } else { result = this.plainTextParser.parse(new StringReader(content)).getChildren().get(0).getChildren(); } } else { result = highlight(parameters, content); } } catch (Exception e) { throw new MacroExecutionException("Failed to highlight content", e); } Map<String, String> formatParameters = new LinkedHashMap<String, String>(); formatParameters.put("class", "code"); if (context.isInline()) { result = Arrays.<Block> asList(new FormatBlock(result, Format.NONE, formatParameters)); } else { result = Arrays.<Block> asList(new GroupBlock(result, formatParameters)); } return result; } /** * Return a highlighted version of the provided content. * * @param parameters the code macro parameters. * @param content the content to highlight. * @return the highlighted version of the provided content. * @throws ParseException the highlight parser failed. * @throws ComponentLookupException failed to find highlight parser for provided language. */ protected List<Block> highlight(CodeMacroParameters parameters, String content) throws ParseException, ComponentLookupException { HighlightParser parser; if (parameters.getLanguage() != null) { if (this.componentManager.hasComponent(HighlightParser.class, parameters.getLanguage())) { try { parser = this.componentManager.getInstance(HighlightParser.class, parameters.getLanguage()); return parser.highlight(parameters.getLanguage(), new StringReader(content)); } catch (ComponentLookupException e) { this.logger.error("Faild to load highlighting parser for language [{}]", parameters.getLanguage(), e); } } } this.logger.debug( "Can't find any specific highlighting parser for language [{}]. Trying the default highlighting parser.", parameters.getLanguage()); parser = this.componentManager.getInstance(HighlightParser.class, "default"); return parser.highlight(parameters.getLanguage(), new StringReader(content)); } }