Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XRENDERING-655: Add the ability to style image captions #258

Merged
merged 24 commits into from Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion xwiki-rendering-api/pom.xml
Expand Up @@ -32,7 +32,7 @@
<packaging>jar</packaging>
<description>XWiki Rendering - Api</description>
<properties>
<xwiki.jacoco.instructionRatio>0.62</xwiki.jacoco.instructionRatio>
<xwiki.jacoco.instructionRatio>0.63</xwiki.jacoco.instructionRatio>
<!-- Skipping revapi since xwiki-rendering-legacy-api wraps this module and runs checks on it -->
<xwiki.revapi.skip>true</xwiki.revapi.skip>
</properties>
Expand Down
@@ -0,0 +1,83 @@
/*
* 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.listener;

import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

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.listener.ListenerProvider;
import org.xwiki.rendering.listener.chaining.ChainingListener;
import org.xwiki.rendering.listener.chaining.ListenerChain;
import org.xwiki.rendering.syntax.Syntax;

import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage;

/**
* Returns a list of {@link ChainingListener} provided by {@link ListenerProvider}.
*
* @version $Id$
* @since 15.3RC1
* @since 14.10.8
*/
@Component(roles = ListenerRegistry.class)
@Singleton
public class ListenerRegistry
{
@Inject
@Named("context")
private Provider<ComponentManager> componentManagerProvider;

@Inject
private Logger logger;

/**
* Return a list of {@link ChainingListener} provided by {@link ListenerProvider}.
*
* @param listenerChain the listener chain in which new listener will be added
* @param action the action performed by the caller ({@link ListenerProvider#PARSE_ACTION} or
* {@link ListenerProvider#RENDER_ACTION})
* @param syntax the syntax of the action (e.g., {@link Syntax#XWIKI_2_1})
* @return the initialized list of {@link ChainingListener}
*/
public List<ChainingListener> getListeners(ListenerChain listenerChain, String action, Syntax syntax)
{
try {
return this.componentManagerProvider.get()
.<ListenerProvider>getInstanceList(ListenerProvider.class)
.stream()
.filter(listenerProvider -> listenerProvider.accept(action, syntax))
.map(listenerProvider -> listenerProvider.getListener(listenerChain))
.collect(Collectors.toList());
} catch (ComponentLookupException e) {
this.logger.warn("Failed to load the list of [{}] for action [{}] and syntax [{}]. Cause [{}].",
ListenerProvider.class, action, syntax, getRootCauseMessage(e));
return List.of();
}
}
}
@@ -0,0 +1,61 @@
/*
* 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.listener;

import org.xwiki.component.annotation.Role;
import org.xwiki.rendering.listener.chaining.ChainingListener;
import org.xwiki.rendering.listener.chaining.ListenerChain;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.stability.Unstable;

/**
* Allows to access listener instances.
*
* @version $Id$
* @since 15.3RC1
* @since 14.10.8
*/
@Role
@Unstable
public interface ListenerProvider
{
/**
* Parse action identifier.
*/
String PARSE_ACTION = "parse";

/**
* Render action identifier.
*/
String RENDER_ACTION = "render";

/**
* @param action the action performed by the listener ("render" or "parse")
* @param syntax the hint of the syntax using for the action
* @return {@code true} when the listener provider can return a listener for the given action and syntaxHint
*/
boolean accept(String action, Syntax syntax);

/**
* @param listenerChain the listener chain in which the listener will be included
* @return the listener to add to the listener chain
*/
ChainingListener getListener(ListenerChain listenerChain);
}
@@ -1,6 +1,7 @@
org.xwiki.rendering.internal.block.BlockMatcherConverter
org.xwiki.rendering.internal.configuration.DefaultRenderingConfiguration
org.xwiki.rendering.internal.converter.DefaultConverter
org.xwiki.rendering.internal.listener.ListenerRegistry
org.xwiki.rendering.internal.listener.MetaDataConverter
org.xwiki.rendering.internal.listener.descriptor.DefaultListenerDescriptorManager
org.xwiki.rendering.internal.parser.reference.GenericLinkReferenceParser
Expand Down
@@ -0,0 +1,134 @@
/*
* 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.listener;

import java.util.List;

import javax.inject.Named;
import javax.inject.Provider;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.rendering.listener.ListenerProvider;
import org.xwiki.rendering.listener.chaining.ChainingListener;
import org.xwiki.rendering.listener.chaining.ListenerChain;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.test.LogLevel;
import org.xwiki.test.junit5.LogCaptureExtension;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;

import ch.qos.logback.classic.Level;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_1;

/**
* Test of {@link ListenerRegistry}.
*
* @version $Id$
*/
@ComponentTest
class ListenerRegistryTest
{
@RegisterExtension
private LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.WARN);

@InjectMockComponents
private ListenerRegistry listenerRegistry;

@MockComponent
@Named("context")
private Provider<ComponentManager> componentManagerProvider;

@Mock
private ComponentManager componentManager;

@Mock
private ListenerChain listenerChain;

@Mock
private ListenerProvider listenerProvider0;

@Mock
private ListenerProvider listenerProvider1;

@Mock
private ChainingListener chainingListener;

@BeforeEach
void setUp()
{
when(this.componentManagerProvider.get()).thenReturn(this.componentManager);
}

@Test
void getListenersNoListenerProvider() throws Exception
{
when(this.componentManager.getInstanceList(ListenerProvider.class)).thenReturn(List.of());
List<ChainingListener> listeners =
this.listenerRegistry.getListeners(this.listenerChain, "action_id", XWIKI_2_1);
assertEquals(List.of(), listeners);
}

@Test
void getListenersComponentLookupException() throws Exception
{
when(this.componentManager.getInstanceList(ListenerProvider.class)).thenThrow(ComponentLookupException.class);
List<ChainingListener> listeners =
this.listenerRegistry.getListeners(this.listenerChain, "action_id", XWIKI_2_1);
assertEquals(List.of(), listeners);
assertEquals("Failed to load the list of [interface org.xwiki.rendering.listener.ListenerProvider] "
+ "for action [action_id] and syntax [XWiki 2.1]. Cause [ComponentLookupException: ].",
this.logCapture.getMessage(0));
assertEquals(Level.WARN, this.logCapture.getLogEvent(0).getLevel());
}

@Test
void getListeners() throws Exception
{
when(this.componentManager.getInstanceList(ListenerProvider.class)).thenReturn(List.of(
this.listenerProvider0,
this.listenerProvider1
));
when(this.listenerProvider0.accept(anyString(), any(Syntax.class))).thenReturn(false);
when(this.listenerProvider1.accept(anyString(), any(Syntax.class))).thenReturn(true);
when(this.listenerProvider1.getListener(this.listenerChain)).thenReturn(this.chainingListener);

String actionId = "action_id";
List<ChainingListener> listeners = this.listenerRegistry.getListeners(this.listenerChain, actionId, XWIKI_2_1);

assertEquals(List.of(this.chainingListener), listeners);
verify(this.listenerProvider0).accept(actionId, XWIKI_2_1);
verify(this.listenerProvider1).accept(actionId, XWIKI_2_1);
verify(this.listenerProvider0, never()).getListener(any());
verify(this.listenerProvider1).getListener(this.listenerChain);
}
}
Expand Up @@ -31,7 +31,7 @@
<name>XWiki Rendering - Syntax - WikiModel Bridge</name>
<description>Bridge WikiModel Model and XWiki Rendering Model</description>
<properties>
<xwiki.jacoco.instructionRatio>0.00</xwiki.jacoco.instructionRatio>
<xwiki.jacoco.instructionRatio>0.04</xwiki.jacoco.instructionRatio>
</properties>
<dependencies>
<dependency>
Expand Down
Expand Up @@ -24,6 +24,7 @@
import javax.inject.Inject;
import javax.inject.Named;

import org.xwiki.component.descriptor.ComponentDescriptor;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.internal.parser.XDOMGeneratorListener;
import org.xwiki.rendering.listener.Listener;
Expand All @@ -50,6 +51,12 @@ public abstract class AbstractWikiModelParser implements Parser, WikiModelStream
@Named("plain/1.0")
protected PrintRendererFactory plainRendererFactory;

@Inject
private ComponentDescriptor<Parser> descriptor;

@Inject
private WikiModelParserListenerBuilder wikiModelParserListenerBuilder;

/**
* @return the WikiModel parser instance to use to parse input content.
* @throws ParseException when there's a problem creating an instance of the parser to use
Expand Down Expand Up @@ -131,7 +138,9 @@ public void parse(Reader source, Listener listener, IdGenerator idGenerator) thr
{
IWikiParser parser = createWikiModelParser();
try {
parser.parse(source, createXWikiGeneratorListener(listener, idGenerator));
parser.parse(source, createXWikiGeneratorListener(
this.wikiModelParserListenerBuilder.buildListener(this.descriptor.getRoleHint(), listener),
idGenerator));
} catch (Exception | StackOverflowError e) {
// Stack overflow errors are caught in addition to exceptions because they can be thrown by javacc based
// implementations in case of too deeply nested contents (e.g., too many nested groups).
Expand Down
Expand Up @@ -740,6 +740,8 @@ public void endParagraph(WikiParameters params)
}
});

// If this should be changed to produce more than just an image directly inside the caption,
// CaptionedImageParseListenerProvider for xwiki-platform needs to be adapted.
getListener().beginFigure(figureParameters);
queue.consumeEvents(getListener());
getListener().beginFigureCaption(Listener.EMPTY_PARAMETERS);
Expand Down