How to split pdf file by book marks using itext 7 , if pdf contains "Duplicate Bookmarks" - duplicates

I am trying to split pdf by its bookmarks using itext7.
Problem : if Pdf is having same bookmark in other place in the outline tree , it is over ridding and unable to split.
Sample code to reproduce the problem:
public void walkOutlines(PdfOutline outline, Map<String, PdfObject> names, PdfDocument pdfDocument,List<String>titles,List<Integer>pageNum) { //----------loop traversing all paths
for (PdfOutline child : outline.getAllChildren()){
if(child.getDestination() != null) {
prepareIndexFile(child,names,pdfDocument,titles,pageNum,list);
}
}
}
//------------Getting pageNumbers from outlines
public void prepareIndexFile(PdfOutline outline, Map<String, PdfObject> names, PdfDocument pdfDocument,List<String>titles,List<Integer>pageNum) {
String title = outline.getTitle();
PdfDestination pdfDestination = outline.getDestination();
String pdfStr = ((PdfString)pdfDestination.getPdfObject()).toUnicodeString();
PdfArray array = (PdfArray) names.get(pdfStr);
PdfObject pdfObj = array != null ? array.get(0) : null;
Integer pageNumber = pdfDocument.getPageNumber((PdfDictionary)pdfObj);
titles.add(title);
pageNum.add(pageNumber);
if(outline.getAllChildren().size() > 0) {
for (PdfOutline child : outline.getAllChildren()){
prepareIndexFile(child,names,pdfDocument,titles,pageNum);
}
}
}
public boolean splitPdf(String inputFile, final String outputFolder) {
boolean splitSuccess = true;
PdfDocument pdfDoc = null;
try {
PdfReader pdfReaderNew = new PdfReader(inputFile);
pdfDoc = new PdfDocument(pdfReaderNew);
final List<String> titles = new ArrayList<String>();
List<Integer> pageNum = new ArrayList<Integer>();
PdfNameTree destsTree = pdfDoc.getCatalog().getNameTree(PdfName.Dests);
Map<String, PdfObject> names = destsTree.getNames();//--------------------------------------Core logic for getting names
PdfOutline root = pdfDoc.getOutlines(false);//--------------------------------------Core logic for getting outlines
walkOutlines(root,names, pdfDoc, titles, pageNum,content); //------Logic to get bookmarks and pageNumbers
if (titles == null || titles.size()==0) {
splitSuccess = false;
}else { //------Proceed if it has bookmarks
for(int i=0;i<titles.size();i++) {
String title = titles.get(i);
String startPageNmStr =""+pageNum.get(i);
int startPage = Integer.parseInt(startPageNmStr);
int endPage = startPage;
if(i == titles.size() - 1) {
endPage = pdfDoc.getNumberOfPages();
}else {
int nextPage = pageNum.get(i+1);
if(nextPage > startPage) {
endPage = nextPage - 1;
}else {
endPage = nextPage;
}
}
String outFileName = outputFolder + File.separator + getFileName(title) + ".pdf";
PdfWriter pdfWriter = new PdfWriter(outFileName);
PdfDocument newDocument = new PdfDocument(pdfWriter, new DocumentProperties().setEventCountingMetaInfo(null));
pdfDoc.copyPagesTo(startPage, endPage, newDocument);
newDocument.close();
pdfWriter.close();
}
}
}catch(Exception e){
//---log
}
}
Found root cause: In PdfNameTree items.put(name.toUnicodeString(), names.get(k));
How to over come this issue?
Thanks in advance

This part of the code:
PdfDestination pdfDestination = outline.getDestination();
String pdfStr = ((PdfString)pdfDestination.getPdfObject()).toUnicodeString();
PdfArray array = (PdfArray) names.get(pdfStr);
PdfObject pdfObj = array != null ? array.get(0) : null;
Integer pageNumber = pdfDocument.getPageNumber((PdfDictionary)pdfObj);
Does not take into account the case that the destination can be non-named and refer to a page explicitly.
So the code needs to be adapted into the following code:
PdfDestination pdfDestination = outline.getDestination();
PdfObject pdfObj = null;
if (pdfDestination.getPdfObject().isString()) {
String pdfStr = ((PdfString) pdfDestination.getPdfObject()).toUnicodeString();
PdfArray array = (PdfArray) names.get(pdfStr);
if (array != null) {
pdfObj = array.get(0);
}
} else if (pdfDestination.getPdfObject().isArray() && ((PdfArray)pdfDestination.getPdfObject()).get(0).isDictionary()) {
pdfObj = ((PdfArray)pdfDestination.getPdfObject()).get(0);
}
Integer pageNumber = pdfDocument.getPageNumber((PdfDictionary)pdfObj);
Additionally, if you want to obtain the full title names including the parent chain, you need to replace String title = outline.getTitle(); with the following piece of code:
String title = outline.getTitle();
PdfOutline parentChain = outline.getParent();
while (parentChain != null) {
title = parentChain.getTitle() + "." + title;
parentChain = parentChain.getParent();
}
As a result, I got 6 files in the output directory, with 5 files of 1 page each and one file of 4 pages.
Complete code:
public void walkOutlines(PdfOutline outline, Map<String, PdfObject> names, PdfDocument pdfDocument,
java.util.List<String>titles,java.util.List<Integer>pageNum) { //----------loop traversing all paths
for (PdfOutline child : outline.getAllChildren()){
if(child.getDestination() != null) {
prepareIndexFile(child,names,pdfDocument,titles,pageNum);
}
}
}
//------------Getting pageNumbers from outlines
public void prepareIndexFile(PdfOutline outline, Map<String, PdfObject> names, PdfDocument pdfDocument,
java.util.List<String>titles,java.util.List<Integer>pageNum) {
String title = outline.getTitle();
PdfOutline parentChain = outline.getParent();
while (parentChain != null) {
title = parentChain.getTitle() + "." + title;
parentChain = parentChain.getParent();
}
PdfDestination pdfDestination = outline.getDestination();
PdfObject pdfObj = null;
if (pdfDestination.getPdfObject().isString()) {
String pdfStr = ((PdfString) pdfDestination.getPdfObject()).toUnicodeString();
PdfArray array = (PdfArray) names.get(pdfStr);
if (array != null) {
pdfObj = array.get(0);
}
} else if (pdfDestination.getPdfObject().isArray() && ((PdfArray)pdfDestination.getPdfObject()).get(0).isDictionary()) {
pdfObj = ((PdfArray)pdfDestination.getPdfObject()).get(0);
}
Integer pageNumber = pdfDocument.getPageNumber((PdfDictionary)pdfObj);
titles.add(title);
pageNum.add(pageNumber);
if(outline.getAllChildren().size() > 0) {
for (PdfOutline child : outline.getAllChildren()){
prepareIndexFile(child,names,pdfDocument,titles,pageNum);
}
}
}
public void splitPdf(String inputFile, final String outputFolder) {
boolean splitSuccess = true;
PdfDocument pdfDoc = null;
try {
PdfReader pdfReaderNew = new PdfReader(inputFile);
pdfDoc = new PdfDocument(pdfReaderNew);
final java.util.List<String> titles = new ArrayList<String>();
java.util.List<Integer> pageNum = new ArrayList<Integer>();
PdfNameTree destsTree = pdfDoc.getCatalog().getNameTree(PdfName.Dests);
Map<String, PdfObject> names = destsTree.getNames();//--------------------------------------Core logic for getting names
PdfOutline root = pdfDoc.getOutlines(false);//--------------------------------------Core logic for getting outlines
walkOutlines(root,names, pdfDoc, titles, pageNum); //------Logic to get bookmarks and pageNumbers
if (titles == null || titles.size()==0) {
splitSuccess = false;
}else { //------Proceed if it has bookmarks
for(int i=0;i<titles.size();i++) {
String title = titles.get(i);
String startPageNmStr =""+pageNum.get(i);
int startPage = Integer.parseInt(startPageNmStr);
int endPage = startPage;
if(i == titles.size() - 1) {
endPage = pdfDoc.getNumberOfPages();
}else {
int nextPage = pageNum.get(i+1);
if(nextPage > startPage) {
endPage = nextPage - 1;
}else {
endPage = nextPage;
}
}
String outFileName = outputFolder + File.separator + title + ".pdf";
PdfWriter pdfWriter = new PdfWriter(outFileName);
PdfDocument newDocument = new PdfDocument(pdfWriter, new DocumentProperties().setEventCountingMetaInfo(null));
pdfDoc.copyPagesTo(startPage, endPage, newDocument);
newDocument.close();
pdfWriter.close();
}
}
}catch(IOException e){
System.out.println(e);
}
}

Related

Serializing and deserializing objects

I am trying to serialize and deserialize my objects to save and load data.
I thought I was smart and introduced Attributes:
[ExposedProperty]
public float Width { get; set; }
[ExposedProperty]
public Color HoverColor { get; set; }
I have a PropertyData class:
[System.Serializable]
public class PropertyData
{
public string Name;
public string Type;
public object Value;
public override string ToString()
{
return "PropertyData ( Name = " + Name + ", Type = " + Type + ", Value = " + Value + ")";
}
}
So instead of writing an ObjectData class for every Object class I have that gets serialized into JSON, I though I'd write a Serializable class that does:
public List<PropertyData> SerializeProperties()
{
var list = new List<PropertyData>();
var type = this.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
.Where(p => p.GetCustomAttributes(typeof(ExposedPropertyAttribute), false).Length > 0)
.ToArray();//
for (int i = 0; i < properties.Length; i++)
{
var property = properties[i];
var data = new PropertyData();
data.Name = property.Name;
data.Type = property.PropertyType.Name;
data.Value = property.GetValue(this);
list.Add(data);
}
return list;
}
and also to deserialize:
protected void DeserializePropertyData(PropertyData data)
{
var p = this.GetType().GetProperty(data.Name);
if (p == null)
{
Debug.LogError("Item " + this + " does not have a property with name '" + data.Name + "'");
return;
}
var type = p.PropertyType;
try
{
//TODO do some magic here to deserialize any of the values.
TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
object propValue = typeConverter.ConvertFromString(data.Value);
p.SetValue(this, propValue);
}
catch(FormatException fe)
{
Debug.Log($"Serializable, there was a format exception on property {data.Name} and value {data.Value} for type {type}");
}
catch(NotSupportedException nse)
{
Debug.Log($"Serializable, there was a not supported exception on property {data.Name} and value {data.Value} for type {type}");
}
finally
{
}
}
But as it turns out, I can't serialize or deserialize Color, or Vector3, or Quaternion, or whatever. It only works for bool, float, string, int...
Any ideas how to serialize/deserialize other objects properly?
Thanks for all the answers. I looked into the ISerializationSurrogate Interface and noticed that it can't be used for UnityEngine.Vector3 or UnityEngine.Color classes.
Then I looked into TypeConverter and also saw that you can't directly use it because you have to add the attribute [TypeConverter(typeof(CustomTypeConverter))] on top of the very class or struct you want to convert.
And at the end of the day, both TypeConverter and ISerializationSurrogate are simply pushing and parsing chars around to get the result. For a Vector3, you have to trim the "(", ")", split the string with a "," and perform float.Parse on every element of the split string array. For a Color, you have to trim "RGBA(" and ")", and do the exact same thing.
Deadline is tight, so I took the same principle and created a static class that does my converting:
public static class MyCustomTypeConverter
{
public static object ConvertFromString(string value, Type destinationType)
{
if (destinationType == typeof(string))
{
return value;
}
else if (destinationType == typeof(int))
{
return int.Parse(value);
}
else if (destinationType == typeof(float))
{
return float.Parse(value);
}
else if (destinationType == typeof(double))
{
return double.Parse(value);
}
else if (destinationType == typeof(long))
{
return long.Parse(value);
}
else if (destinationType == typeof(bool))
{
return bool.Parse(value);
}
else if (destinationType == typeof(Vector3))
{
var mid = value.Substring(1, value.Length - 2);
var values = mid.Split(",");
return new Vector3(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(Vector4))
{
var mid = value.Substring(1, value.Length - 2);
var values = mid.Split(",");
return new Vector4(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture),
float.Parse(values[3], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(Color))
{
var mid = value.Substring(5, value.Length - 6);
var values = mid.Split(",");
return new Color(
float.Parse(values[0], CultureInfo.InvariantCulture),
float.Parse(values[1], CultureInfo.InvariantCulture),
float.Parse(values[2], CultureInfo.InvariantCulture),
float.Parse(values[3], CultureInfo.InvariantCulture)
);
}
else if (destinationType == typeof(PrimitiveType))
{
var e = Enum.Parse<PrimitiveType>(value);
return e;
}
else if (destinationType == typeof(SupportedLightType))
{
var e = Enum.Parse<SupportedLightType>(value);
return e;
}
return null;
}
public static string ConvertToString(object value)
{
return value.ToString();
}
}
It works, does what I want, and I can extend it for further classes or structs. Quaternion or Bounds perhaps, but I don't need much more than that.
My Serialization class now does this:
public List<PropertyData> SerializeProperties()
{
var list = new List<PropertyData>();
var type = this.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty)
.Where(p => p.GetCustomAttributes(typeof(ExposedPropertyAttribute), false).Length > 0)
.ToArray();//
for (int i = 0; i < properties.Length; i++)
{
var property = properties[i];
var data = new PropertyData();
data.Name = property.Name;
data.Value = MyCustomTypeConverter.ConvertToString(property.GetValue(this));
list.Add(data);
}
return list;
}
and that:
protected void DeserializePropertyData(PropertyData data)
{
var p = this.GetType().GetProperty(data.Name);
if (p == null)
{
return;
}
var type = p.PropertyType;
object propValue = MyCustomTypeConverter.ConvertFromString(data.Value, type);
p.SetValue(this, propValue);
}

Read arbitrarily json data to a javafx treeview,and only show the first element of any array in it

I need to show a json file on a javafx treeview,the structure of the json is unknown.Like the web site: json viewer site
I show the tree for user to select path of a value(like xpath of xml),so if the json is too big,I only need to show the first element of any array in json.
for example,the original data is:
{
name:"tom",
schools:[
{
name:"school1",
tags:["maths","english"]
},
{
name:"school2",
tags:["english","biological"]
},
]
}
I want to show:
again:the structure of json is unknown,it is just one example.
There's no other option than recursively handling the json and create the TreeItem structure based on the element info.
(There's probably a better way of adding the symbols, but I didn't find appropriate icons.)
private static final String INPUT = "{\n"
+ " name:\"tom\",\n"
+ " schools:[\n"
+ " {\n"
+ " name:\"school1\",\n"
+ " tags:[\"maths\",\"english\"]\n"
+ " },\n"
+ " {\n"
+ " name:\"school2\",\n"
+ " tags:[\"english\",\"biological\"]\n"
+ " },\n"
+ " ]\n"
+ "}";
private static final Image JSON_IMAGE = new Image("https://i.stack.imgur.com/1slrh.png");
private static void prependString(TreeItem<Value> item, String string) {
String val = item.getValue().text;
item.getValue().text = (val == null
? string
: string + " : " + val);
}
private enum Type {
OBJECT(new Rectangle2D(45, 52, 16, 18)),
ARRAY(new Rectangle2D(61, 88, 16, 18)),
PROPERTY(new Rectangle2D(31, 13, 16, 18));
private final Rectangle2D viewport;
private Type(Rectangle2D viewport) {
this.viewport = viewport;
}
}
private static final class Value {
private String text;
private final Type type;
public Value(Type type) {
this.type = type;
}
public Value(String text, Type type) {
this.text = text;
this.type = type;
}
}
private static TreeItem<Value> createTree(JsonElement element) {
if (element.isJsonNull()) {
return new TreeItem<>(new Value("null", Type.PROPERTY));
} else if (element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
return new TreeItem<>(new Value(primitive.isString()
? '"' + primitive.getAsString() + '"'
: primitive.getAsString(), Type.PROPERTY));
} else if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
TreeItem<Value> item = new TreeItem<>(new Value(Type.ARRAY));
// for (int i = 0, max = Math.min(1, array.size()); i < max; i++) {
for (int i = 0, max = array.size(); i < max; i++) {
TreeItem<Value> child = createTree(array.get(i));
prependString(child, Integer.toString(i));
item.getChildren().add(child);
}
return item;
} else {
JsonObject object = element.getAsJsonObject();
TreeItem<Value> item = new TreeItem<>(new Value(Type.OBJECT));
for (Map.Entry<String, JsonElement> property : object.entrySet()) {
TreeItem<Value> child = createTree(property.getValue());
prependString(child, property.getKey());
item.getChildren().add(child);
}
return item;
}
}
#Override
public void start(Stage primaryStage) {
JsonParser parser = new JsonParser();
JsonElement root = parser.parse(INPUT);
TreeItem<Value> treeRoot = createTree(root);
TreeView<Value> treeView = new TreeView<>(treeRoot);
treeView.setCellFactory(tv -> new TreeCell<Value>() {
private final ImageView imageView;
{
imageView = new ImageView(JSON_IMAGE);
imageView.setFitHeight(18);
imageView.setFitWidth(16);
imageView.setPreserveRatio(true);
setGraphic(imageView);
}
#Override
protected void updateItem(Value item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
imageView.setVisible(false);
} else {
setText(item.text);
imageView.setVisible(true);
imageView.setViewport(item.type.viewport);
}
}
});
final Scene scene = new Scene(treeView);
primaryStage.setScene(scene);
primaryStage.show();
}

JSP tag library to display MySQL rollup query with grouping and subtotals

I need to display several tables as HTML, using JSP, coming from MySQL GROUP BY a,b,c WITH ROLLUP queries. I'm looking for a good tag library to achieve this. I have found DisplayTag, but it'was last updated in 2008. And I would prefer using the subtotals calculated by MySQL, which seems to be tricky with DisplayTag.
MySQL does subtotals by adding extra rows to the resultset with the group field set to NULL.
Is there a better alternative? Printing the table is important, paging and sorting would be nice but I can live without them. No editing of any kind.
I wrote my own quick-and-dirty tag. Note that it expects the rollup data structure returned by MySQL, haven't tested it with anything else.
Usage example:
<xxx:rollupTable cssClass="data" data="${data}">
<xxx:rollupColumn title="Person" align="left" group="true" fieldName="personName" groupFieldName="personId" tooltipLink="person"/>
<xxx:rollupColumn title="City" align="left" group="true" fieldName="cityName" groupFieldName="cityId" tooltipLink="city"/>
<xxx:rollupColumn title="Price" align="right" format="#,##0.000" fieldName="price"/>
<xxx:rollupColumn title="Amount" align="right" format="#,##0" fieldName="amount"/>
</xxx:rollupTable>
The column tag does not much but adds the column definition to the table tag for later use.
package xxx.tags;
import...
public class RollupTableColumnTag extends SimpleTagSupport {
private String title;
private boolean group = false;
private boolean sum = false;
private String fieldName; // field name to output
private String groupFieldName; // field name to test for rollup level changes
private String align;
private String format;
private String tooltipLink;
private DecimalFormat formatter;
public void doTag() throws IOException, JspTagException {
RollupTableTag parent = (RollupTableTag)findAncestorWithClass(this, RollupTableTag.class);
if (parent == null) {
throw new JspTagException("Parent tag not found.");
}
parent.addColumnDefinition(this);
}
public void setFormat(String format) {
formatter = new DecimalFormat(format);
this.format = format;
}
public DecimalFormat getFormatter() {
return formatter;
}
// other getters and setters are standard, excluded
}
The table tag does the actual hard work:
package xxx.tags;
import ...
public class RollupTableTag extends BodyTagSupport {
protected String cssClass;
protected List<Map> data;
protected List<RollupTableColumnTag> columns;
protected List<Integer> groups;
public void setCssClass(String cssClass) {
this.cssClass = cssClass;
}
public void setData(List data) {
this.data = (List<Map>)data;
}
public int doStartTag() throws JspException {
columns = new ArrayList<RollupTableColumnTag>();
groups = new ArrayList<Integer>();
return EVAL_BODY_BUFFERED;
}
public int doEndTag() throws JspException {
try {
JspWriter writer = pageContext.getOut();
if (data.size() == 0) {
writer.println("<P>No data.</P>");
return EVAL_PAGE;
}
int nLevels = groups.size();
int nNormalRowCount = 0;
boolean[] bStartGroup = new boolean[nLevels];
String[] sSummaryTitle = new String[nLevels];
for (int i=0;i<nLevels;i++) {
bStartGroup[i] = true;
}
writer.println("<TABLE class=\"" + cssClass + "\">");
writer.println("<THEAD><TR>");
for (RollupTableColumnTag column : columns) {
writer.print("<TH");
if (column.getAlign() != null) {
writer.print(" align=\"" + column.getAlign() + "\"");
}
writer.print(">" + column.getTitle() + "</TH>");
}
writer.println("</TR></THEAD>");
writer.println("<TBODY>");
for (Map dataRow : data) {
StringBuffer out = new StringBuffer();
out.append("<TR>");
// grouping columns always come first
String cellClass = null;
for (int i=0;i<nLevels-1;i++) {
if (bStartGroup[i]) {
Object dataField = dataRow.get(columns.get(groups.get(i)).getFieldName());
sSummaryTitle[i] = dataField == null ? "" : dataField.toString();
}
}
int nLevelChanges = 0;
for (int i=0;i<nLevels;i++) {
if (dataRow.get( columns.get(groups.get(i)).getGroupFieldName() ) == null) {
if (i>0) {
bStartGroup[i-1] = true;
}
nLevelChanges++;
}
}
int nTotalLevel = nLevels - nLevelChanges;
if (nLevelChanges == nLevels) { // grand total row
cellClass = "grandtotal";
addCell(out, "Grand Total:", null, cellClass, nLevelChanges);
} else if (nLevelChanges > 0) { // other total row
boolean isOneLiner = (nNormalRowCount == 1);
nNormalRowCount = 0;
if (isOneLiner) continue; // skip one-line sums
cellClass = "total"+nTotalLevel;
for (int i=0;i<nLevels-nLevelChanges-1;i++) {
addCell(out," ",null,cellClass, 1);
}
addCell(out, sSummaryTitle[nLevels-nLevelChanges-1] + " total:", null, cellClass, nLevelChanges+1);
} else { // normal row
for (int i=0;i<nLevels;i++) {
if (bStartGroup[i]) {
RollupTableColumnTag column = columns.get(groups.get(i));
Object cellData = dataRow.get(column.getFieldName());
String displayVal = cellData != null ? cellData.toString() : "[n/a]";
if (column.getTooltipLink() != null && !column.getTooltipLink().isEmpty() && cellData != null) {
String tooltip = column.getTooltipLink();
int dataid = Integer.parseInt(dataRow.get(column.getGroupFieldName()).toString());
displayVal = "<div ajaxtooltip=\"" + tooltip + "\" ajaxtooltipid=\"" + dataid + "\">" + displayVal + "</div>";
}
addCell(out, displayVal, column.getAlign(), null, 1);
} else {
addCell(out," ", null, null, 1);
}
}
for (int i=0;i<nLevels-1;i++) {
bStartGroup[i] = false;
}
nNormalRowCount++;
}
// other columns
for (RollupTableColumnTag column : columns) {
if (!column.isGroup()) {
Object content = dataRow.get(column.getFieldName());
String displayVal = "";
if (content != null) {
if (column.getFormat() != null) {
float val = Float.parseFloat(content.toString());
displayVal = column.getFormatter().format(val);
} else {
displayVal = content.toString();
}
}
addCell(out,displayVal,column.getAlign(),cellClass,1);
}
}
out.append("</TR>");
// empty row for better readability
if (groups.size() > 2 && nLevelChanges == groups.size() - 1) {
out.append("<TR><TD colspan=\"" + columns.size() + "\"> </TD>");
}
writer.println(out);
}
writer.println("</TBODY>");
writer.println("</TABLE>");
} catch (IOException e) {
e.printStackTrace();
}
return EVAL_PAGE;
}
public void addCell(StringBuffer out, String content, String align, String cssClass, int colSpan) {
out.append("<TD");
if (align != null) {
out.append(" align=\"" + align + "\"");
}
if (cssClass != null) {
out.append(" class=\"" + cssClass + "\"");
}
if (colSpan > 1) {
out.append(" colspan=\"" + colSpan + "\"");
}
out.append(">");
out.append(content);
out.append("</TD>");
}
public void addColumnDefinition(RollupTableColumnTag cd) {
columns.add(cd);
if (cd.isGroup()) groups.add(columns.size()-1);
}
}

PrimeFaces 5.0 datatable filter case sensitive

In PrimeFaces 5.0, datatable filter is case sensitive. In Primefaces 4 the filter was not case sensitive. But now in 5.0 my app doesn't work.
DataTable Filtering v. 5.0.1
Filtering is case sensitive in 5.0, due to feedback it is now case insensitive. FilterEvent was also providing wrong information about the filters, it has been corrected as well.
http://blog.primefaces.org/?p=3184
You can use the filterFunction option, like this:
In XHTML:
<p:column ...
filterBy="#{car.name}"
filterFunction="#{carListView.filterByName}"
In View:
public boolean filterByName(Object value, Object filter, Locale locale) {
String filterText = (filter == null) ? null : filter.toString().trim();
if (filterText == null || filterText.equals("")) {
return true;
}
if (value == null) {
return false;
}
String carName = value.toString().toUpperCase();
filterText = filterText.toUpperCase();
if (carName.contains(filterText)) {
return true;
} else {
return false;
}
}
This worked for me. Please refer to: http://www.primefaces.org/showcase/ui/data/datatable/filter.xhtml (price column). And also page 153 at the PrimeFaces 5.0 manual (pdf).
I have been facing the same problem.
But I fix it replacing the filter class of primefaces.
1- create a package in you project: org.primefaces.component.datatable.feature
2 - create a class: FilterFeature
I've included this part on the class:
// FIX bug primefaces 5.0
// The filter should be case insensitive
if(columnValue != null && filterValue!= null){
columnValue = columnValue.toString().toUpperCase();
filterValue = filterValue.toString().toUpperCase();
}
Copy the contents of this class to your file.
FilterFeature.java
/*
* Copyright 2009-2014 PrimeTek.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.primefaces.component.datatable.feature;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import org.primefaces.component.api.UIColumn;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import org.primefaces.component.api.DynamicColumn;
import org.primefaces.component.column.Column;
import org.primefaces.component.columngroup.ColumnGroup;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.component.datatable.DataTableRenderer;
import org.primefaces.component.row.Row;
import org.primefaces.context.RequestContext;
import org.primefaces.model.filter.*;
import org.primefaces.util.Constants;
public class FilterFeature implements DataTableFeature {
private final static Logger logger = Logger.getLogger(DataTable.class.getName());
private final static String STARTS_WITH_MATCH_MODE = "startsWith";
private final static String ENDS_WITH_MATCH_MODE = "endsWith";
private final static String CONTAINS_MATCH_MODE = "contains";
private final static String EXACT_MATCH_MODE = "exact";
private final static String LESS_THAN_MODE = "lt";
private final static String LESS_THAN_EQUALS_MODE = "lte";
private final static String GREATER_THAN_MODE = "gt";
private final static String GREATER_THAN_EQUALS_MODE = "gte";
private final static String EQUALS_MODE = "equals";
private final static String IN_MODE = "in";
private final static String GLOBAL_MODE = "global";
final static Map<String,FilterConstraint> FILTER_CONSTRAINTS;
static {
FILTER_CONSTRAINTS = new HashMap<String,FilterConstraint>();
FILTER_CONSTRAINTS.put(STARTS_WITH_MATCH_MODE, new StartsWithFilterConstraint());
FILTER_CONSTRAINTS.put(ENDS_WITH_MATCH_MODE, new EndsWithFilterConstraint());
FILTER_CONSTRAINTS.put(CONTAINS_MATCH_MODE, new ContainsFilterConstraint());
FILTER_CONSTRAINTS.put(EXACT_MATCH_MODE, new ExactFilterConstraint());
FILTER_CONSTRAINTS.put(LESS_THAN_MODE, new LessThanFilterConstraint());
FILTER_CONSTRAINTS.put(LESS_THAN_EQUALS_MODE, new LessThanEqualsFilterConstraint());
FILTER_CONSTRAINTS.put(GREATER_THAN_MODE, new GreaterThanFilterConstraint());
FILTER_CONSTRAINTS.put(GREATER_THAN_EQUALS_MODE, new GreaterThanEqualsFilterConstraint());
FILTER_CONSTRAINTS.put(EQUALS_MODE, new EqualsFilterConstraint());
FILTER_CONSTRAINTS.put(IN_MODE, new InFilterConstraint());
FILTER_CONSTRAINTS.put(GLOBAL_MODE, new GlobalFilterConstraint());
}
private boolean isFilterRequest(FacesContext context, DataTable table) {
return context.getExternalContext().getRequestParameterMap().containsKey(table.getClientId(context) + "_filtering");
}
public boolean shouldDecode(FacesContext context, DataTable table) {
return false;
}
public boolean shouldEncode(FacesContext context, DataTable table) {
return isFilterRequest(context, table);
}
public void decode(FacesContext context, DataTable table) {
String globalFilterParam = table.getClientId(context) + UINamingContainer.getSeparatorChar(context) + "globalFilter";
List<FilterMeta> filterMetadata = this.populateFilterMetaData(context, table);
Map<String,Object> filterParameterMap = this.populateFilterParameterMap(context, table, filterMetadata, globalFilterParam);
table.setFilters(filterParameterMap);
table.setFilterMetadata(filterMetadata);
}
public void encode(FacesContext context, DataTableRenderer renderer, DataTable table) throws IOException {
//reset state
updateFilteredValue(context, table, null);
table.setFirst(0);
table.setRowIndex(-1);
if(table.isLazy()) {
table.loadLazyData();
}
else {
String globalFilterParam = table.getClientId(context) + UINamingContainer.getSeparatorChar(context) + "globalFilter";
filter(context, table, table.getFilterMetadata(), globalFilterParam);
//sort new filtered data to restore sort state
boolean sorted = (table.getValueExpression("sortBy") != null || table.getSortBy() != null);
if(sorted) {
SortFeature sortFeature = (SortFeature) table.getFeature(DataTableFeatureKey.SORT);
if(table.isMultiSort())
sortFeature.multiSort(context, table);
else
sortFeature.singleSort(context, table);
}
}
renderer.encodeTbody(context, table, true);
}
private void filter(FacesContext context, DataTable table, List<FilterMeta> filterMetadata, String globalFilterParam) {
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
List filteredData = new ArrayList();
Locale filterLocale = table.resolveDataLocale();
boolean hasGlobalFilter = params.containsKey(globalFilterParam);
String globalFilterValue = hasGlobalFilter ? params.get(globalFilterParam): null;
GlobalFilterConstraint globalFilterConstraint = (GlobalFilterConstraint) FILTER_CONSTRAINTS.get(GLOBAL_MODE);
ELContext elContext = context.getELContext();
for(int i = 0; i < table.getRowCount(); i++) {
table.setRowIndex(i);
boolean localMatch = true;
boolean globalMatch = false;
for(FilterMeta filterMeta : filterMetadata) {
Object filterValue = filterMeta.getFilterValue();
UIColumn column = filterMeta.getColumn();
MethodExpression filterFunction = column.getFilterFunction();
ValueExpression filterByVE = filterMeta.getFilterByVE();
if(column instanceof DynamicColumn) {
((DynamicColumn) column).applyStatelessModel();
}
Object columnValue = filterByVE.getValue(elContext);
FilterConstraint filterConstraint = this.getFilterConstraint(column);
// FIX bug primefaces 5.0
// The filter should be case insensitive
if(columnValue != null && filterValue!= null){
columnValue = columnValue.toString().toUpperCase();
filterValue = filterValue.toString().toUpperCase();
}
if(hasGlobalFilter && !globalMatch) {
globalMatch = globalFilterConstraint.applies(columnValue, globalFilterValue, filterLocale);
}
if(filterFunction != null) {
localMatch = (Boolean) filterFunction.invoke(elContext, new Object[]{columnValue, filterValue, filterLocale});
}
else if(!filterConstraint.applies(columnValue, filterValue, filterLocale)) {
localMatch = false;
}
if(!localMatch) {
break;
}
}
boolean matches = localMatch;
if(hasGlobalFilter) {
matches = localMatch && globalMatch;
}
if(matches) {
filteredData.add(table.getRowData());
}
}
//Metadata for callback
if(table.isPaginator()) {
RequestContext requestContext = RequestContext.getCurrentInstance();
if(requestContext != null) {
requestContext.addCallbackParam("totalRecords", filteredData.size());
}
}
//save filtered data
updateFilteredValue(context, table, filteredData);
table.setRowIndex(-1); //reset datamodel
}
public void updateFilteredValue(FacesContext context, DataTable table, List<?> value) {
table.setSelectableDataModelWrapper(null);
ValueExpression ve = table.getValueExpression("filteredValue");
if(ve != null) {
ve.setValue(context.getELContext(), value);
}
else {
if(value != null) {
logger.log(Level.WARNING, "DataTable {0} has filtering enabled but no filteredValue model reference is defined"
+ ", for backward compatibility falling back to page viewstate method to keep filteredValue."
+ " It is highly suggested to use filtering with a filteredValue model reference as viewstate method is deprecated and will be removed in future."
, new Object[]{table.getClientId(context)});
}
table.setFilteredValue(value);
}
}
private Map<String,Object> populateFilterParameterMap(FacesContext context, DataTable table, List<FilterMeta> filterMetadata, String globalFilterParam) {
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
Map<String,Object> filterParameterMap = new HashMap<String, Object>();
for(FilterMeta filterMeta : filterMetadata) {
Object filterValue = filterMeta.getFilterValue();
UIColumn column = filterMeta.getColumn();
if(filterValue != null && !filterValue.toString().trim().equals(Constants.EMPTY_STRING)) {
String filterField = null;
ValueExpression filterByVE = column.getValueExpression("filterBy");
if(column.isDynamic()) {
((DynamicColumn) column).applyStatelessModel();
Object filterByProperty = column.getFilterBy();
String field = column.getField();
if(field == null)
filterField = (filterByProperty == null) ? table.resolveDynamicField(filterByVE) : filterByProperty.toString();
else
filterField = field;
}
else {
String field = column.getField();
if(field == null)
filterField = (filterByVE == null) ? (String) column.getFilterBy(): table.resolveStaticField(filterByVE);
else
filterField = field;
}
filterParameterMap.put(filterField, filterValue);
}
}
if(params.containsKey(globalFilterParam)) {
filterParameterMap.put("globalFilter", params.get(globalFilterParam));
}
return filterParameterMap;
}
private List<FilterMeta> populateFilterMetaData(FacesContext context, DataTable table) {
List<FilterMeta> filterMetadata = new ArrayList<FilterMeta>();
String separator = String.valueOf(UINamingContainer.getSeparatorChar(context));
String var = table.getVar();
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
ColumnGroup group = getColumnGroup(table, "header");
if(group != null) {
for(UIComponent child : group.getChildren()) {
Row headerRow = (Row) child;
if(headerRow.isRendered()) {
for(UIComponent headerRowChild : headerRow.getChildren()) {
Column column = (Column) headerRowChild;
if(column.isRendered()) {
ValueExpression columnFilterByVE = column.getValueExpression("filterBy");
Object filterByProperty = column.getFilterBy();
if(columnFilterByVE != null || filterByProperty != null) {
ValueExpression filterByVE = (columnFilterByVE != null) ? columnFilterByVE : createFilterByVE(context, var, filterByProperty);
UIComponent filterFacet = column.getFacet("filter");
Object filterValue;
if(filterFacet == null)
filterValue = params.get(column.getClientId(context) + separator + "filter");
else
filterValue = ((ValueHolder) filterFacet).getLocalValue();
filterMetadata.add(new FilterMeta(column, filterByVE, filterValue));
}
}
}
}
}
}
else {
for(UIColumn column : table.getColumns()) {
ValueExpression columnFilterByVE = column.getValueExpression("filterBy");
Object filterByProperty = column.getFilterBy();
if (columnFilterByVE != null || filterByProperty != null) {
UIComponent filterFacet = column.getFacet("filter");
Object filterValue = null;
ValueExpression filterByVE = null;
String filterId = null;
if(column instanceof Column) {
filterByVE = (columnFilterByVE != null) ? columnFilterByVE : createFilterByVE(context, var, filterByProperty);
filterId = column.getClientId(context) + separator + "filter";
}
else if(column instanceof DynamicColumn) {
DynamicColumn dynamicColumn = (DynamicColumn) column;
dynamicColumn.applyStatelessModel();
filterByProperty = column.getFilterBy();
filterByVE = (filterByProperty == null) ? columnFilterByVE : createFilterByVE(context, var, filterByProperty);
filterId = dynamicColumn.getContainerClientId(context) + separator + "filter";
dynamicColumn.cleanStatelessModel();
}
if(filterFacet == null)
filterValue = params.get(filterId);
else
filterValue = ((ValueHolder) filterFacet).getLocalValue();
filterMetadata.add(new FilterMeta(column, filterByVE, filterValue));
}
}
}
return filterMetadata;
}
private ColumnGroup getColumnGroup(DataTable table, String target) {
for(UIComponent child : table.getChildren()) {
if(child instanceof ColumnGroup) {
ColumnGroup colGroup = (ColumnGroup) child;
String type = colGroup.getType();
if(type != null && type.equals(target)) {
return colGroup;
}
}
}
return null;
}
public FilterConstraint getFilterConstraint(UIColumn column) {
String filterMatchMode = column.getFilterMatchMode();
FilterConstraint filterConstraint = FILTER_CONSTRAINTS.get(filterMatchMode);
if(filterConstraint == null) {
throw new FacesException("Illegal filter match mode:" + filterMatchMode);
}
return filterConstraint;
}
private ValueExpression createFilterByVE(FacesContext context, String var, Object filterBy) {
ELContext elContext = context.getELContext();
return context.getApplication().getExpressionFactory().createValueExpression(elContext, "#{" + var + "." + filterBy + "}", Object.class);
}
private class FilterMeta {
private UIColumn column;
private ValueExpression filterByVE;
private Object filterValue;
public FilterMeta(UIColumn column, ValueExpression filterByVE, Object filterValue) {
this.column = column;
this.filterByVE = filterByVE;
this.filterValue = filterValue;
}
public UIColumn getColumn() {
return column;
}
public ValueExpression getFilterByVE() {
return filterByVE;
}
public Object getFilterValue() {
return filterValue;
}
}
}
I took a little more simplistic approach to get past this in my situation.
In my XHTML:
<p:column filterBy="#{car.nameLowercase}" sortBy="#{car.name}"
filterMatchMode="contains">
<h:outputText value="#{car.name}"/>
</p:column>
In my car bean I simply add a getter:
public String getNameLowerCase() {
if (name == null) {
return null;
}
return name.toLowerCase();
}
This seems to work for my purposes. Hope it will help you!
I had same issue on the Lazy.xhtml, I fixied it by go to the LazyCarDataModel.java, on load method, change
if(filterValue==null ||
fieldvalue.startswith(filtervalue.toString()))
to
if(filterValue==null ||
fieldvalue.startswith(filtervalue.toString().toLowerCase() ||
fieldvalue.startswith(filtervalue.toString().toUpperCase()) )

HTML Diff tool API [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I'm looking for an api that will visually show html difference for both structure, characters/words, and style. This tool must also support double byte characters and be flexible enough for me to add it to my existing website to show the results of the comparison easily. I'm currently using the Component Software COM implementation which doesn't support double byte characters and hasn't been updated in about six years.
The only two tools I found that can do something like that are http://changedetection.com and http://imnosy.com. Both offer you to specify a url and watch them for changes.
This is what I used:
[http://code.google.com/p/google-diff-match-patch/][1]
I had to write my own methods to do the compare but after a little work it looks fine. This implementation compares test as passed in so it works fine if you are just comparing 2 text strings. My diff_prettyHtml call was changed to:
public string diff_prettyHtml(List<Diff> diffs)
{
StringBuilder html = new StringBuilder();
foreach (Diff aDiff in diffs)
{
string text = aDiff.text.Replace("&", "&").Replace("<", "<")
.Replace(">", ">").Replace("\n", "<br>");
switch (aDiff.operation)
{
case Operation.INSERT:
html.Append("<ins class='diff'>").Append(text)
.Append("</ins>");
break;
case Operation.DELETE:
html.Append("<del class='diff'>").Append(text)
.Append("</del>");
break;
case Operation.EQUAL:
html.Append("<span>").Append(text).Append("</span>");
break;
}
}
return html.ToString();
}
Now if you want to do a compare preview of 2 html strings this is a little different. This is what I did:
DiffMatchPatch.diff_match_patch diff = new DiffMatchPatch.diff_match_patch();
List<DiffMatchPatch.Diff> differences = diff.diff_main(oldHtml,
newHtml);
return diff.diff_previewHtml(differences);
public string diff_previewHtml(List<Diff> diffs) {
StringBuilder html = new StringBuilder();
foreach (Diff aDiff in diffs) {
string text = aDiff.text;
switch (aDiff.operation) {
case Operation.INSERT:
html.Append("<ins class='diff'>").Append(text)
.Append("</ins>");
break;
case Operation.DELETE:
html.Append("<del class='diff'>").Append(text)
.Append("</del>");
break;
case Operation.EQUAL:
html.Append(text);
break;
}
}
return html.ToString();
}
The unicode class is as follows:
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
namespace HtmlCompare
{
class Unicoder
{
private Hashtable _htmlHash = new Hashtable();
private const string _htmlPattern = #"<(S*?)[^>]*>.*?|<.*?\/>";
private List<string> _blockElements = "img,br".Split(',').ToList<string>();
private int _currentHash = 44032;
public string pushHash(string tag)
{
if (_htmlHash[tag] == null)
{
//_htmlHash[tag] = char.Parse("\\u" + Convert.ToString(_currentHash,16));
_htmlHash[tag] = char.ConvertFromUtf32(_currentHash);
_currentHash++;
}
return _htmlHash[tag].ToString();
}
private string tagMatch(Match tag)
{
return pushHash(tag.Value);
}
public string html2plain(string html)
{
MatchEvaluator tagEvaluator = new MatchEvaluator(tagMatch);
return Regex.Replace(html, _htmlPattern, tagEvaluator, RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
private string ProcessDiffTag(string tagStart, string tagEnd, string contents)
{
ArrayList diffTagParts = new ArrayList();
MatchCollection matches = Regex.Matches(contents,
_htmlPattern,
RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (matches.Count > 0)
{
int contentsStringIndex = 0;
int contentsStringEndIndex = 0;
int lastContentStringIndex = 0;
bool lastTag = false;
TagDefinition definition;
foreach (Match currentMatch in matches)
{
contentsStringIndex = currentMatch.Index;
contentsStringEndIndex = contentsStringIndex + currentMatch.Length;
lastTag = (currentMatch == matches[matches.Count - 1]);
// did we miss text that isn't a tag?
if (contentsStringIndex > lastContentStringIndex)
{
definition = new TagDefinition();
definition.Tag = false;
definition.Text = contents.Substring(lastContentStringIndex, contentsStringIndex - lastContentStringIndex);
AddTagDefinition(diffTagParts, definition);
}
else if (lastTag && contents.Length > contentsStringEndIndex) // something after the last tag?
{
definition = new TagDefinition();
definition.Tag = false;
definition.Text = contents.Substring(contentsStringEndIndex, contents.Length - contentsStringEndIndex);
AddTagDefinition(diffTagParts, definition);
}
// work on current tag
definition = new TagDefinition();
definition.Tag = true;
definition.OpeningTag = !IsClosingTag(currentMatch.Value);
definition.TagType = GetTagType(currentMatch.Value);
definition.Text = currentMatch.Value;
AddTagDefinition(diffTagParts, definition);
lastContentStringIndex = contentsStringEndIndex;
}
return GoThroughDiffParts(diffTagParts,
tagStart,
tagEnd);
}
else
return string.Concat(tagStart, contents, tagEnd);
}
private string GetTagType(string tag)
{
int startIndex = 1; // skip <
if (tag.StartsWith("</"))
startIndex = 2; // skip </
int endIndex = tag.IndexOf(" ");
if (endIndex == -1)
endIndex = tag.IndexOf(">");
return tag.Substring(startIndex, endIndex - startIndex);
}
private string GoThroughDiffParts(ArrayList parts, string startTag, string endTag)
{
IEnumerator enumerator = parts.GetEnumerator();
StringBuilder before = new StringBuilder(string.Empty);
StringBuilder middle = new StringBuilder(string.Empty);
StringBuilder after = new StringBuilder(string.Empty);
TagDefinition definition;
while (enumerator.MoveNext())
{
definition = (TagDefinition)enumerator.Current;
if (!definition.Used) // have we already used this part?
{
definition.Used = true;
if (_blockElements.Contains(definition.TagType))
middle.Append(definition.Text);
else if (definition.MatchingIndex == -1) // no matching tag
{
if (definition.Tag) // html tag?
{
if (definition.OpeningTag)
before.Append(definition.Text);
else
after.Append(definition.Text);
}
else
middle.Append(definition.Text);
}
else
{
if (!definition.Tag) // text and has a matching tag
{
TagDefinition matchingTag = (TagDefinition)parts[definition.MatchingIndex];
if (matchingTag.OpeningTag)
matchingTag.Text += definition.Text;
else
matchingTag.Text = string.Concat(definition.Text, matchingTag.Text);
definition.Used = true;
}
else
middle.Append(definition.Text);
}
}
}
bool includeDiffTag = true;
if (string.IsNullOrEmpty(middle.ToString()))
includeDiffTag = false; // we don't want the ins/del tag around nothing
else if (string.IsNullOrWhiteSpace(middle.ToString())) // spacing should be kept
middle = new StringBuilder(" " + middle.Replace("\n", "<br />"));
if(includeDiffTag)
middle.Insert(0, startTag); // <ins>[middle]
middle.Insert(0, before); // [before]<ins>[middle]
if (includeDiffTag)
middle.Append(endTag); // [before]<ins>[middle]</ins>
middle.Append(after); // [before]<ins>[middle]</ins>[end]
return middle.ToString();
}
private string DiffTagMatch(Match tag)
{
string tagStart = tag.Groups[1].Value;
string tagEnd = tag.Groups[5].Value;
string contents = tag.Groups[4].Value;
if (string.IsNullOrEmpty(contents))
return string.Empty; // we don't want the ins/del tag around nothing
else if (string.IsNullOrWhiteSpace(contents)) // spacing should be kept
return string.Concat(tagStart, " ", contents.Replace("\n", "<br />"), tagEnd);
else
return ProcessDiffTag(tagStart,
tagEnd,
contents);
}
private bool IsClosingTag(string tag)
{
return tag.Contains("</") && !tag.ToLower().Contains("<img") && !tag.ToLower().Contains("<br");
}
public string CleanUpMisplacedDiffTags(string html)
{
return Regex.Replace(html, #"(\<((ins|del).*?)\>)(.*?)(\<\/((ins|del).*?)\>)", DiffTagMatch, RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
public string plain2html(string plain)
{
IDictionaryEnumerator enumerator = _htmlHash.GetEnumerator();
while (enumerator.MoveNext())
{
plain = Regex.Replace(plain,
_htmlHash[enumerator.Key].ToString(),
enumerator.Key.ToString(),
RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
return CleanUpMisplacedDiffTags(plain);
}
private void AddTagDefinition(ArrayList list, TagDefinition tag)
{
IEnumerator enumerator = list.GetEnumerator();
TagDefinition currentDefinition;
int index = 0;
int insertingIndex = list.Count;
while (enumerator.MoveNext())
{
currentDefinition = (TagDefinition)enumerator.Current;
//if (!tag.OpeningTag && currentDefinition.MatchingIndex == -1)
// currentDefinition.MatchingIndex = insertingIndex;
if (tag.MatchingIndex == -1 && // matching tag not found yet
(currentDefinition.OpeningTag && !tag.OpeningTag) && // opening & closing
currentDefinition.TagType == currentDefinition.TagType) // same tag type
{
tag.MatchingIndex = index;
currentDefinition.MatchingIndex = insertingIndex;
}
}
list.Add(tag);
}
private class TagDefinition
{
public bool Tag { get; set; }
public string TagType { get; set; }
public string Text { get; set; }
public int MatchingIndex { get; set; }
public bool OpeningTag { get; set; }
public bool Used { get; set; }
public TagDefinition()
{
this.Tag = false;
this.Text = string.Empty;
this.TagType = string.Empty;
this.MatchingIndex = -1;
this.OpeningTag = false;
this.Used = false;
}
}
}
}