Skip to content

Commit 37bc135

Browse files
committed
CONFLUENCE-463: Propagate the style attribute of col element to table cells, allowing to migrate column sizes
1 parent 93ec51a commit 37bc135

File tree

10 files changed

+603
-48
lines changed

10 files changed

+603
-48
lines changed

confluence-syntax-xhtml/src/main/java/org/xwiki/contrib/confluence/parser/xhtml/internal/ConfluenceXHTMLParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceListItemTagHandler;
6060
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceOrderedListTagHandler;
6161
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceParagraphTagHandler;
62+
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTableColHandler;
63+
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTableRowTagHandler;
64+
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTableTagHandler;
6265
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceUnorderedListTagHandler;
6366
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXHTMLWhitespaceXMLFilter;
6467
import org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXWikiGeneratorListener;
@@ -250,6 +253,9 @@ public IWikiParser createWikiModelParser() throws ParseException
250253

251254
handlers.put("ri:attachment", new AttachmentTagHandler(refConverter));
252255

256+
handlers.put("table", new ConfluenceTableTagHandler());
257+
handlers.put("col", new ConfluenceTableColHandler());
258+
handlers.put("tr", new ConfluenceTableRowTagHandler());
253259
handlers.put("th", new TableHeadTagHandler());
254260
handlers.put("td", new TableCellTagHandler());
255261

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel;
21+
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.Map;
25+
26+
import org.apache.commons.lang3.StringUtils;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
import org.xwiki.rendering.wikimodel.WikiParameter;
30+
import org.xwiki.rendering.wikimodel.WikiParameters;
31+
import org.xwiki.rendering.wikimodel.xhtml.handler.TagHandler;
32+
import org.xwiki.rendering.wikimodel.xhtml.impl.TagContext;
33+
34+
import static org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTagHandler.CONFLUENCE_TABLE_COLUMN_ATTRIBUTES;
35+
36+
/**
37+
* Table tag col handler.
38+
* @since 9.85.0
39+
* @version $Id$
40+
*/
41+
public class ConfluenceTableColHandler extends TagHandler
42+
{
43+
private static final Logger LOGGER = LoggerFactory.getLogger(ConfluenceTableColHandler.class);
44+
45+
private static final String STYLE = "style";
46+
47+
/**
48+
* Constructor.
49+
*/
50+
public ConfluenceTableColHandler()
51+
{
52+
super(false);
53+
}
54+
55+
@Override
56+
protected void begin(TagContext context)
57+
{
58+
super.begin(context);
59+
String style = null;
60+
WikiParameters params = context.getParams();
61+
WikiParameter styleParam = params.getParameter(STYLE);
62+
if (styleParam != null) {
63+
style = styleParam.getValue();
64+
}
65+
66+
Map<String, String> attributes = StringUtils.isEmpty(style) ? Collections.emptyMap() : Map.of(STYLE, style);
67+
int span = 1;
68+
WikiParameter spanParam = params.getParameter("span");
69+
if (spanParam != null) {
70+
try {
71+
span = Integer.parseInt(spanParam.getValue());
72+
} catch (Exception e) {
73+
LOGGER.error("Could not parse the span parameter of the col element (value: [{}]), expected an integer",
74+
spanParam.getValue(), e);
75+
}
76+
}
77+
78+
List<Map<String, String>> tableAttributes =
79+
(List<Map<String, String>>) context.getTagStack().getStackParameter(CONFLUENCE_TABLE_COLUMN_ATTRIBUTES);
80+
81+
for (int i = 0; i < span; i++) {
82+
tableAttributes.add(attributes);
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel;
21+
22+
import org.xwiki.rendering.wikimodel.xhtml.handler.TableRowTagHandler;
23+
import org.xwiki.rendering.wikimodel.xhtml.impl.TagContext;
24+
25+
import static org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTagHandler.CONFLUENCE_TABLE_CURRENT_COL;
26+
27+
/**
28+
* Table row tag handler.
29+
* @since 9.85.0
30+
* @version $Id$
31+
*/
32+
public class ConfluenceTableRowTagHandler extends TableRowTagHandler
33+
{
34+
/**
35+
* Constructor.
36+
*/
37+
public ConfluenceTableRowTagHandler()
38+
{
39+
super();
40+
}
41+
42+
@Override
43+
protected void begin(TagContext context)
44+
{
45+
context.getTagStack().pushStackParameter(CONFLUENCE_TABLE_CURRENT_COL, 0);
46+
super.begin(context);
47+
}
48+
49+
@Override
50+
protected void end(TagContext context)
51+
{
52+
context.getTagStack().popStackParameter(CONFLUENCE_TABLE_CURRENT_COL);
53+
super.end(context);
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel;
21+
22+
import java.util.ArrayList;
23+
import java.util.Map;
24+
25+
import org.xwiki.rendering.wikimodel.xhtml.handler.TableTagHandler;
26+
import org.xwiki.rendering.wikimodel.xhtml.impl.TagContext;
27+
28+
import static org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTagHandler.CONFLUENCE_TABLE_COLUMN_ATTRIBUTES;
29+
30+
/**
31+
* Table tag handler.
32+
* @since 9.85.0
33+
* @version $Id$
34+
*/
35+
public class ConfluenceTableTagHandler extends TableTagHandler
36+
{
37+
/**
38+
* Constructor.
39+
*/
40+
public ConfluenceTableTagHandler()
41+
{
42+
super();
43+
}
44+
45+
@Override
46+
protected void begin(TagContext context)
47+
{
48+
context.getTagStack().pushStackParameter(CONFLUENCE_TABLE_COLUMN_ATTRIBUTES,
49+
new ArrayList<Map<String, String>>());
50+
super.begin(context);
51+
}
52+
53+
@Override
54+
protected void end(TagContext context)
55+
{
56+
context.getTagStack().popStackParameter(CONFLUENCE_TABLE_COLUMN_ATTRIBUTES);
57+
super.end(context);
58+
}
59+
}

confluence-syntax-xhtml/src/main/java/org/xwiki/contrib/confluence/parser/xhtml/internal/wikimodel/ConfluenceTagHandler.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ public interface ConfluenceTagHandler
3333
*/
3434
String CONFLUENCE_CONTAINER = "confluence-container";
3535

36+
/**
37+
* Stack parameter key used in tables, so styles from colgroup / col tags can be propagated to the cells.
38+
*/
39+
String CONFLUENCE_TABLE_COLUMN_ATTRIBUTES = "confluence-table-column-attributes";
40+
/**
41+
* Stack parameter key used in table rows, to know which is the current column when handling a td or th element.
42+
*/
43+
String CONFLUENCE_TABLE_CURRENT_COL = "confluence-table-current-col";
3644
/**
3745
* Stack parameter key used if in a paragraph.
3846
*/

confluence-syntax-xhtml/src/main/java/org/xwiki/contrib/confluence/parser/xhtml/internal/wikimodel/TableCellTagHandler.java

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,20 @@
1919
*/
2020
package org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel;
2121

22+
import java.util.List;
23+
import java.util.Map;
24+
25+
import org.apache.commons.lang3.StringUtils;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
import org.xwiki.rendering.wikimodel.WikiParameter;
29+
import org.xwiki.rendering.wikimodel.WikiParameters;
2230
import org.xwiki.rendering.wikimodel.xhtml.handler.TableDataTagHandler;
2331
import org.xwiki.rendering.wikimodel.xhtml.impl.TagContext;
2432

33+
import static org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTagHandler.CONFLUENCE_TABLE_COLUMN_ATTRIBUTES;
34+
import static org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceTagHandler.CONFLUENCE_TABLE_CURRENT_COL;
35+
2536
/**
2637
* Make sure to produce something that won't break xwiki/2.x table. See https://jira.xwiki.org/browse/XRENDERING-488
2738
*
@@ -30,19 +41,101 @@
3041
*/
3142
public class TableCellTagHandler extends TableDataTagHandler
3243
{
44+
private static final Logger LOGGER = LoggerFactory.getLogger(TableCellTagHandler.class);
45+
3346
@Override
3447
protected void begin(TagContext context)
3548
{
36-
super.begin(context);
37-
49+
beginCell(context);
3850
beginDocument(context);
3951
}
4052

53+
static void beginCell(TagContext context)
54+
{
55+
WikiParameters wikiParameters = fetchColumnStyle(context);
56+
context.getScannerContext().beginTableCell(
57+
context.isTag("th"),
58+
wikiParameters);
59+
}
60+
4161
@Override
4262
protected void end(TagContext context)
4363
{
4464
endDocument(context);
45-
65+
advanceCurrentCol(context, LOGGER);
4666
super.end(context);
4767
}
68+
69+
static WikiParameters fetchColumnStyle(TagContext context)
70+
{
71+
Map<String, String> attributes = fetchColumnAttributes(context);
72+
if (attributes == null) {
73+
return context.getParams();
74+
}
75+
76+
for (Map.Entry<String, String> attr : attributes.entrySet()) {
77+
String attrName = attr.getKey();
78+
WikiParameter existingParam = context.getParams().getParameter(attrName);
79+
boolean isStyle = "style".equals(attrName);
80+
if (isStyle) {
81+
// merge the styles, giving precedence to the more specific one by putting it at the end
82+
String existingStyle = existingParam == null ? "" : existingParam.getValue();
83+
String newStyle = StringUtils.isEmpty(existingStyle)
84+
? attr.getValue()
85+
: removeSemicolon(attr.getValue()) + "; " + existingStyle;
86+
return context.getParams().setParameter(attrName, newStyle);
87+
} else if (existingParam == null) {
88+
// we don't overwrite the more specific attributes
89+
return context.getParams().setParameter(attrName, attr.getValue());
90+
}
91+
}
92+
return context.getParams();
93+
}
94+
95+
private static String removeSemicolon(String value)
96+
{
97+
String v = value.trim();
98+
if (v.endsWith(";")) {
99+
v = v.substring(0, v.length() - 1).trim();
100+
}
101+
return v;
102+
}
103+
104+
private static Map<String, String> fetchColumnAttributes(TagContext context)
105+
{
106+
Integer currentCol = (Integer) context.getTagStack().getStackParameter(CONFLUENCE_TABLE_CURRENT_COL);
107+
if (currentCol == null) {
108+
return null;
109+
}
110+
List<Map<String, String>> tableAttributes =
111+
(List<Map<String, String>>) context.getTagStack().getStackParameter(CONFLUENCE_TABLE_COLUMN_ATTRIBUTES);
112+
if (tableAttributes == null) {
113+
return null;
114+
}
115+
if (currentCol >= tableAttributes.size()) {
116+
return null;
117+
}
118+
return tableAttributes.get(currentCol);
119+
}
120+
121+
static void advanceCurrentCol(TagContext context, Logger logger)
122+
{
123+
Integer currentCol = (Integer) context.getTagStack().getStackParameter(CONFLUENCE_TABLE_CURRENT_COL);
124+
if (currentCol == null) {
125+
LOGGER.error("Could not determine the current column. This should not happen.");
126+
return;
127+
}
128+
int colspan = 1;
129+
WikiParameter spanParam = context.getParams().getParameter("colspan");
130+
if (spanParam != null) {
131+
try {
132+
colspan = Integer.parseInt(spanParam.getValue());
133+
} catch (Exception e) {
134+
logger.error("Could not parse the colspan parameter of the cell (value: [{}]), expected an integer",
135+
spanParam.getValue(), e);
136+
}
137+
}
138+
139+
context.getTagStack().setStackParameter(CONFLUENCE_TABLE_CURRENT_COL, currentCol + colspan);
140+
}
48141
}

0 commit comments

Comments
 (0)