/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2014 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.server.ngclient.template;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringEscapeUtils;
import org.json.JSONObject;
import org.jsoup.helper.StringUtil;
import org.sablo.specification.NGPackageSpecification;
import org.sablo.specification.WebComponentSpecProvider;
import org.sablo.specification.WebLayoutSpecification;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IFormElement;
import com.servoy.j2db.persistence.IPersist;
import com.servoy.j2db.persistence.IRepository;
import com.servoy.j2db.persistence.LayoutContainer;
import com.servoy.j2db.persistence.PositionComparator;
import com.servoy.j2db.server.ngclient.FormElementHelper;
import com.servoy.j2db.server.ngclient.ServoyDataConverterContext;
import com.servoy.j2db.util.Debug;
/**
* Generates HTML for a flow layout form
* @author jblok, lvostinar
*/
@SuppressWarnings("nls")
public class FormLayoutStructureGenerator
{
public static void generateLayout(Form form, String realFormName, ServoyDataConverterContext context, PrintWriter writer, boolean design, boolean highlight)
{
try
{
FormLayoutGenerator.generateFormStartTag(writer, form, realFormName, false, design);
Iterator<IPersist> components = form.getAllObjects(PositionComparator.XY_PERSIST_COMPARATOR);
while (components.hasNext())
{
IPersist component = components.next();
if (component instanceof LayoutContainer)
{
generateLayoutContainer((LayoutContainer)component, form, context, writer, design, highlight);
}
else if (component instanceof IFormElement)
{
FormLayoutGenerator.generateFormElement(writer, FormElementHelper.INSTANCE.getFormElement((IFormElement)component, context, null), form,
design, highlight);
}
}
FormLayoutGenerator.generateFormEndTag(writer);
}
catch (Exception e)
{
Debug.error(e);
}
}
private static void generateLayoutContainer(LayoutContainer container, Form form, ServoyDataConverterContext context, PrintWriter writer, boolean design,
boolean highlight) throws IOException
{
if (highlight) writer.print("<div class='highlight_element'>");
writer.print("<");
writer.print(container.getTagType());
if (design)
{
writer.print(" svy-id='");
writer.print(container.getUUID().toString());
writer.print("'");
NGPackageSpecification<WebLayoutSpecification> pkg = WebComponentSpecProvider.getInstance().getLayoutSpecifications().get(
container.getPackageName());
WebLayoutSpecification spec = null;
if (pkg != null && (spec = pkg.getSpecification(container.getSpecName())) != null)
{
List<String> allowedChildren = spec.getAllowedChildren();
if (allowedChildren.size() > 0)
{
writer.print(" svy-allowed-children='");
writer.print("[" + StringUtil.join(allowedChildren, ",") + "]");
writer.print("'");
}
writer.print(" svy-layoutname='");
writer.print(spec.getName());
writer.print("'");
JSONObject ngClass = new JSONObject();
ngClass.put("svy-layoutcontainer", true);
if (!(container.getAncestor(IRepository.FORMS).getID() == form.getID()))//is this inherited?
{
ngClass.put("inheritedElement", true);
}
if (spec.getDesignStyleClass() != null && spec.getDesignStyleClass().length() > 0)
{
ngClass.put(spec.getDesignStyleClass(), "<showWireframe==true<");//added <> tokens so that we can remove quotes around the values so that angular will evaluate at runtime
}
if (ngClass.length() > 0) writer.print(" ng-class='" + ngClass.toString().replaceAll("\"<", "").replaceAll("<\"", "") + "'");
}
}
else
{
writer.print(" ng-class=\"'svy-layoutcontainer'\" ");
}
if (container.getElementId() != null)
{
writer.print(" id='");
writer.print(container.getElementId());
writer.print("' ");
}
writer.print(" svy-autosave ");
Map<String, String> attributes = container.getAttributes();
if (attributes != null)
{
for (Entry<String, String> entry : attributes.entrySet())
{
writer.print(" ");
StringEscapeUtils.ESCAPE_ECMASCRIPT.translate(entry.getKey(), writer);
if (entry.getValue() != null && entry.getValue().length() > 0)
{
writer.print("=\"");
StringEscapeUtils.ESCAPE_ECMASCRIPT.translate(entry.getValue(), writer);
writer.print("\"");
}
}
}
writer.println(">");
Iterator<IPersist> components = container.getAllObjects(PositionComparator.XY_PERSIST_COMPARATOR);
while (components.hasNext())
{
IPersist component = components.next();
if (component instanceof LayoutContainer)
{
generateLayoutContainer((LayoutContainer)component, form, context, writer, design, highlight);
}
else if (component instanceof IFormElement)
{
FormLayoutGenerator.generateFormElement(writer, FormElementHelper.INSTANCE.getFormElement((IFormElement)component, context, null), form, design,
highlight);
}
}
writer.print("</");
writer.print(container.getTagType());
writer.print(">");
if (highlight) writer.print("</div>");
}
// /**
// * @param form
// * @param fs
// * @param writer
// */
// public static void generate(Form form, ServoyDataConverterContext context, PrintWriter writer)
// {
// try
// {
// Map<String, FormElement> allFormElements = new HashMap<String, FormElement>();
//
// Iterator<IFormElement> it = form.getFormElementsSortedByFormIndex();
// while (it.hasNext())
// {
// IFormElement element = it.next();
// FormElement fe = ComponentFactory.getFormElement(element, context, null);
// allFormElements.put(element.getUUID().toString(), fe);
//
// //make life easy if a real name is used
// if (element.getName() != null) allFormElements.put(element.getName(), fe);
// }
//
// HTMLParser parser = new HTMLParser(form.getLayoutGrid());
// List<MarkupElement> elems = parser.parse();
//
// writer.println(String.format("<div ng-controller=\"%1$s\" ng-style=\"formStyle\" svy-layout-update>", form.getName()));
//
// XmlTag skipUntilClosed = null;
// Iterator<MarkupElement> elem_it = elems.iterator();
// while (elem_it.hasNext())
// {
// XmlTag tag = (XmlTag)elem_it.next();
// if (skipUntilClosed != null)
// {
// if (!tag.closes(skipUntilClosed)) continue;
// skipUntilClosed = null;
// continue;
// }
//
// IValueMap attributes = tag.getAttributes();
// String id = attributes.getString("id");
// FormElement fe = allFormElements.get(id);
// if (fe != null)
// {
//// if (fe.getTagname().equals(tag.getName())) does not need to be same, if id matches we replace
// {
// writer.print(fe.toString());
// if (!tag.isOpenClose()) skipUntilClosed = tag;
// }
// }
// else
// {
// writer.print(tag.toCharSequence());
// }
// }
// writer.println("</div>");
// }
// catch (Exception e)
// {
// Debug.error(e);
// }
// }
// /**
// * Merge of wicket MarkupParser,HTMLHandler,Markup (since not usable without running inside wicket)
// * @author Jan Blok
// */
// public static class HTMLParser
// {
// /** Map of simple tags. */
// private static final Map<String, Boolean> doesNotRequireCloseTag = new HashMap<String, Boolean>();
//
// static
// {
// // Tags which are allowed not be closed in HTML
// doesNotRequireCloseTag.put("p", Boolean.TRUE);
// doesNotRequireCloseTag.put("br", Boolean.TRUE);
// doesNotRequireCloseTag.put("img", Boolean.TRUE);
// doesNotRequireCloseTag.put("input", Boolean.TRUE);
// doesNotRequireCloseTag.put("hr", Boolean.TRUE);
// doesNotRequireCloseTag.put("link", Boolean.TRUE);
// doesNotRequireCloseTag.put("meta", Boolean.TRUE);
// }
//
// private final IXmlPullParser xmlParser;
// private final List<MarkupElement> markupElements;
//
// public HTMLParser(final String markup) throws IOException, ResourceStreamNotFoundException
// {
// xmlParser = new XmlPullParser();
// xmlParser.parse(markup);
//
// markupElements = new ArrayList<MarkupElement>();
// }
//
// public List<MarkupElement> parse() throws ParseException
// {
// // Loop through parser tags
// MarkupElement me = null;
// while ((me = xmlParser.nextTag()) != null)
// {
// markupElements.add(me);
// }
//
// // Loop through tags
// ArrayListStack<XmlTag> stack = new ArrayListStack<XmlTag>();
// Iterator<MarkupElement> it = markupElements.iterator();
// while (it.hasNext())
// {
// XmlTag tag = (XmlTag)it.next();
//
// // Check tag type
// if (tag.isOpen())
// {
// // Push onto stack
// stack.push(tag);
// }
// else if (tag.isClose())
// {
// // Check that there is something on the stack
// if (stack.size() > 0)
// {
// // Pop the top tag off the stack
// XmlTag top = stack.pop();
//
// // If the name of the current close tag does not match the
// // tag on the stack then we may have a mismatched close tag
// boolean mismatch = !top.hasEqualTagName(tag);
//
// if (mismatch)
// {
// // Pop any simple tags off the top of the stack
// while (mismatch && !requiresCloseTag(top.getName()))
// {
// // mark them as open/close
// top.setType(XmlTag.OPEN_CLOSE);
//
// // Pop simple tag
// if (stack.isEmpty())
// {
// break;
// }
// top = stack.pop();
//
// // Does new top of stack mismatch too?
// mismatch = !top.hasEqualTagName(tag);
// }
//
// // If adjusting for simple tags did not fix the problem,
// // it must be a real mismatch.
// if (mismatch)
// {
// throw new ParseException("Tag " + top.toUserDebugString() + " has a mismatched close tag at " + tag.toUserDebugString(),
// top.getPos());
// }
// }
//
// // Tag matches, so add pointer to matching tag
// tag.setOpenTag(top);
// }
// else
// {
// throw new ParseException("Tag " + tag.toUserDebugString() + " does not have a matching open tag", tag.getPos());
// }
// }
// else if (tag.isOpenClose())
// {
// // Tag closes itself
// tag.setOpenTag(tag);
// }
// }
//
// return markupElements;
// }
//
// /**
// * Gets whether this tag does not require a closing tag.
// *
// * @param name
// * The tag's name, e.g. a, br, div, etc.
// * @return True if this tag does not require a closing tag
// */
// public static boolean requiresCloseTag(final String name)
// {
// return doesNotRequireCloseTag.get(name.toLowerCase()) == null;
// }
// }
}