/*
* 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.refactoring.splitter.criterion.naming;
import java.util.ArrayList;
import java.util.List;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.BlockFilter;
import org.xwiki.rendering.block.HeaderBlock;
import org.xwiki.rendering.block.SpaceBlock;
import org.xwiki.rendering.block.SpecialSymbolBlock;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.renderer.BlockRenderer;
import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
import org.xwiki.rendering.renderer.printer.WikiPrinter;
/**
* A {@link NamingCriterion} based on the opening heading (if present) of the document.
*
* @version $Id: 134008a0c3e8cb0f6e2e878bd6ad4486c9e3a1f2 $
* @since 1.9M1
*/
public class HeadingNameNamingCriterion implements NamingCriterion
{
/**
* Used to render block to plain text.
*/
private BlockRenderer plainSyntaxRenderer;
/**
* {@link DocumentAccessBridge} used to lookup for existing wiki pages and avoid name clashes.
*/
private DocumentAccessBridge docBridge;
/**
* In case if we cannot find a heading name present in the document, we will revert back to
* {@link PageIndexNamingCriterion}.
*/
private NamingCriterion mainPageNameAndNumberingNamingCriterion;
/**
* A list containing all the document names generated so far. This is used to avoid name clashes.
*/
private List<String> documentNames;
/**
* Name of the base page name.
*/
private String basePageName;
/**
* Space name to be used with generated page names.
*/
private String spaceName;
/**
* Flag indicating if each generated page name should be prepended with base page name.
*/
private boolean prependBasePageName;
/**
* Constructs a new {@link HeadingNameNamingCriterion}.
*
* @param baseDocumentName name of the document that is being split.
* @param docBridge {@link DocumentAccessBridge} used to lookup for documents.
* @param plainSyntaxRenderer the renderer to convert to plain text
* @param prependBasePageName a flag indicating if each generated page name should be prepended with base page name.
*/
public HeadingNameNamingCriterion(String baseDocumentName, DocumentAccessBridge docBridge,
BlockRenderer plainSyntaxRenderer, boolean prependBasePageName)
{
this.mainPageNameAndNumberingNamingCriterion = new PageIndexNamingCriterion(baseDocumentName, docBridge);
this.docBridge = docBridge;
this.plainSyntaxRenderer = plainSyntaxRenderer;
this.documentNames = new ArrayList<String>();
int dot = baseDocumentName.lastIndexOf('.');
this.spaceName = (dot != -1) ? baseDocumentName.substring(0, dot) : "Main";
this.basePageName = baseDocumentName.substring(dot + 1);
this.prependBasePageName = prependBasePageName;
}
@Override
public String getDocumentName(XDOM newDoc)
{
String documentName = null;
String prefix = spaceName + ".";
if (newDoc.getChildren().size() > 0) {
Block firstChild = newDoc.getChildren().get(0);
if (firstChild instanceof HeaderBlock) {
// Clone the header block and remove any unwanted stuff
Block clonedHeaderBlock = firstChild.clone(new BlockFilter()
{
public List<Block> filter(Block block)
{
List<Block> blocks = new ArrayList<Block>();
if (block instanceof WordBlock || block instanceof SpaceBlock
|| block instanceof SpecialSymbolBlock) {
blocks.add(block);
}
return blocks;
}
});
XDOM xdom = new XDOM(clonedHeaderBlock.getChildren());
WikiPrinter printer = new DefaultWikiPrinter();
this.plainSyntaxRenderer.render(xdom, printer);
documentName = cleanPageName(printer.toString());
}
}
// Fall back if necessary.
if (null == documentName || documentName.equals("")) {
documentName = mainPageNameAndNumberingNamingCriterion.getDocumentName(newDoc);
} else if (prependBasePageName) {
documentName = prefix + basePageName + INDEX_SEPERATOR + documentName;
} else {
documentName = prefix + documentName;
}
// Truncate long document names.
int maxWidth = (documentNames.contains(documentName) || docBridge.exists(documentName)) ? 252 : 255;
if (documentName.length() > maxWidth) {
documentName = documentName.substring(0, maxWidth);
}
// Resolve any name clashes.
String newDocumentName = documentName;
int localIndex = 0;
while (documentNames.contains(newDocumentName) || docBridge.exists(newDocumentName)) {
// Append a trailing local index if the page already exists
newDocumentName = documentName + INDEX_SEPERATOR + (++localIndex);
}
// Add the newly generated document name into the pool of generated document names.
documentNames.add(newDocumentName);
return newDocumentName;
}
/**
* Utility method for cleaning out invalid / dangerous characters from page names.
*
* @param originalName the page name to be cleaned.
* @return the cleaned page name.
*/
private String cleanPageName(String originalName)
{
// These characters are reserved for xwiki internal use.
String replaced = originalName.trim().replaceAll("[\\.:]", "-");
// Links to documents containing these characters are not rendered correctly at the moment.
replaced = replaced.replaceAll("[@?#~/]", "");
return replaced;
}
}