Omit empty attributes with groovy DOMBuilder - dom

Groovy's MarkupBuilder has an omitNullAttributes and an omitEmptyAttributes. But DOMBuilder doesn't. This is the code I have
>>> def xml = DOMBuilder.newInstance()
>>> def maybeEmpty = null
>>> println xml.foo(bar: maybeEmpty)
<foo bar=""/>
I want bar to be omitted if empty. I found a workaround in an answer to Groovy AntBuilder, omit conditional attributes... to findAll empty attributes and remove them. Since I have a complex DOM to be generated, I'm looking for other options.

I believe there is no built-in option for that, but if you need a DOMBuilder, you could subclass it and filter the attributes...
#groovy.transform.InheritConstructors
class DOMBuilderSubclass extends groovy.xml.DOMBuilder {
#Override
protected Object createNode(Object name, Map attributes) {
super.createNode name, attributes.findAll{it.value != null}
}
}
You might want to tune the construction as in standard DOMBuilder, this is just an example.
def factory = groovy.xml.FactorySupport.createDocumentBuilderFactory().newDocumentBuilder()
def builder = new DOMBuilderSubclass(factory)
println builder.foo(bar: null, baz: 1)
//<?xml version="1.0" encoding="UTF-8"?>
//<foo baz="1"/>
standard output as you said was...
println groovy.xml.DOMBuilder.newInstance().foo(bar: null, baz: 1)
//<?xml version="1.0" encoding="UTF-8"?>
//<foo bar="" baz="1"/>

Related

File Template extension with IntelliJ plugin

So I want to create a file template in my IntelliJ IDEA plugin and I managed to mine these out from some other plugins, but the template always wants to be a java file and I cannot change the extension of it to be .cqt insteaf of .java
What I figured out:
I need to add these to the plugin.xml
<internalFileTemplate name="Croquette File" />
and I need
<createFromTemplateHandler implementation="...CroquetteCreateFromTemplateHandler" />
with the file creation action
<action
id="Croquette.NewFile"
class="...NewCroquetteFileDefinitionAction"
text="Croquette File"
description="Create a new Croquette file" >
<add-to-group group-id="NewGroup" anchor="after" relative-to-action="NewGroup1"/>
</action>
The NewCroquetteFileDefinitionAction contains this:
class NewCroquetteFileDefinitionAction
extends CreateFileFromTemplateAction(
NewCroquetteFileDefinitionAction.NEW_CQT_FILE,
"",
CroquetteIcons.fileIcon)
with DumbAware {
import NewCroquetteFileDefinitionAction._
override def buildDialog(
project: Project,
directory: PsiDirectory,
builder: CreateFileFromTemplateDialog.Builder): Unit = {
builder
.setTitle(NEW_CQT_FILE)
.addKind("Croquette File", CroquetteIcons.fileIcon, "Croquette File")
.setValidator(new InputValidatorEx {
override def getErrorText(inputString: String): String =
if (!inputString.isEmpty && !containsFuckyCharacters(inputString))
s"'$inputString' is not a valid Croquette file name."
else null
override def checkInput(inputString: String): Boolean = getErrorText(inputString) != null
override def canClose(inputString: String): Boolean = checkInput(inputString)
})
}
override def getActionName(directory: PsiDirectory, newName: String, templateName: String): String = NEW_CQT_FILE
override def hashCode(): Int = 0
override def equals(obj: Any): Boolean = obj.isInstanceOf[NewCroquetteFileDefinitionAction]
}
object NewCroquetteFileDefinitionAction {
val NEW_CQT_FILE = "New Croquette File"
def containsFuckyCharacters(strIn: String): Boolean = {
List("+", "-", "<", ">", "\\", "/", ",", ".", "[", "]", "{", "}", "#", "&", "$", ";", "*").foreach(ch =>
if (strIn.contains(ch))
return true
)
false
}
}
And I am guessing I have to do something here in the CroquetteDefaultPropertiesProvider class but not sure what, or even if that guess is correct or not as I cannot find any documentation on how to create file templates with plugins.
class CroquetteDefaultPropertiesProvider extends TemplatePackagePropertyProvider {
override def fillProperties(directory: PsiDirectory, props: Properties) {
super.fillProperties(directory, props)
}
}
You need to put some Name.your-lang-extension.ft files under fileTemplates package, like this, and declare <internalFileTemplate name="Name" />, like this (this Name corresponds to the Name in Name.your-lang-extension.ft).
In the template files you may use some variables, there're some predefined ones and you can use your own. The syntax is ${MY_VAR}.
Then you should implement buildDialog like this, the third argument to addKind is the Name in Name.your-lang-extension.ft, the first one is the displayed name.
Then you should create a action class extending CreateFileFromTemplateAction, like this, and create a Properties instance by using FileTemplateManager.getInstance(project).defaultProperties like this and add some properties like this which will be filled in to the variables, like you can properties.add("MY_VAR", yourCodeToGetSomeInformation()) and ${MY_VAR} in your template will the replaced with the return value of yourCodeToGetSomeInformation().
After that you should implement createFileFromTemplate like this, the key part is the call to CreateFromTemplateDialog's create(), just look at my code example and you'll know.
After implementing the action class you should register it into plugin.xml just like what you're doing now.
BTW please remove your dangerously incorrect implementation of equals and hashCode.
You don't need a createFromTemplateHandler.

Wicket NumberTextField in Kotlin throws ClassCastException when submitted

I'm having some issues with a Wicket (8.0.0-M4) NumberTextField in Kotlin (1.1.0).
My stripped-down form looks like this:
class Test : AbstractWebPage() {
val housenumberModel: Model<Int> = Model<Int>()
val housenumber = NumberTextField<Int>("housenumberModel", housenumberModel)
val form: Form<Unit> = object : Form<Unit>("adressForm") {}
override fun onInitialize() {
super.onInitialize()
form.add(housenumber.setRequired(false))
form.add(object : SubmitLink("submit") {
override fun onSubmit() {
super.onSubmit()
println(housenumberModel.`object`) // this is line 28
}
})
add(form)
}
}
After submitting the form I get the following stacktrace:
java.lang.ClassCastException: java.lang.String cannot be cast to
java.lang.Number
at com.mycompany.test.pages.Test$onInitialize$1.onSubmit(Test.kt:28)
at org.apache.wicket.markup.html.form.Form.delegateSubmit(Form.java:1312)
at org.apache.wicket.markup.html.form.Form.process(Form.java:979)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:802)
at org.apache.wicket.markup.html.form.Form.onRequest(Form.java:715)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.internalInvoke(ListenerRequestHandler.java:301)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.invoke(ListenerRequestHandler.java:250)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.invokeListener(ListenerRequestHandler.java:210)
at org.apache.wicket.core.request.handler.ListenerRequestHandler.respond(ListenerRequestHandler.java:203)
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:912)
at org.apache.wicket.request.RequestHandlerExecutor.execute(RequestHandlerExecutor.java:65)
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:283)
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:253)
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:221)
at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:262)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:204)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:286)
[...]
If I use
val housenumberModel: Model<Int> = Model.of(0)
instead of
val housenumberModel: Model<Int> = Model<Int>()
everything works fine. But since my NumberTextField is optional I don't want to have it pre-initialized with 0.
Me and my colleagues were trying to change the type signature of the Model in every way we could imagine but came to no solution. A co-worker suggested to write a custom Wicket converter since Kotlins Int is represendeted as a primitive type (From the docs: "On the JVM, non-nullable values of this type are represented as values of the primitive type int.") Even though I don't know yet if this would work it seems like an overkill for me.
Another hack I could think of: writing some JavaScript to delete the zero from the input field. Also not really something I would want to do.
Question: Is there a simple solution to my problem?
(And as a bonus-question: has already anyone written a larger Wicket application in Kotlin and could tell me if this combination is ready for prime time to develop a critical project with this stack or is my problem just the tip of the iceberg?)
[edit]
Solution as pointed out by svenmeier:
Using
val housenumber = NumberTextField<Int>("housenumberModel", housenumberModel, Int::class.java)
works.
Or as an alternative:
val housenumbervalue: Int? = null
val housenumberModel: IModel<Int> = PropertyModel<Int>(this, "housenumbervalue")
val housenumber = NumberTextField<Int>("housenumberModel", housenumberModel)
Because of type erasure your NumberTextField cannot detect the generic type parameter of your model. Since your model object is null, it cannot be used to derive the type either.
In this case Wicket assumes a String model object type :/.
Either provide the type to the NumberTextField explicitly, or use a model that keeps its generic information, e.g. a PropertyModel.
There is a way to tell wicket about the type you want, it is by adding the type in the constructor. More here.
In Java it looks like this:
new NumberTextField<Integer>("housenumberModel", housenumberModel, Integer.class);

plone.formwidget - Is it possible to set a MasterSelect Field as an AutocompleteFieldWidget?

I am trying to set a MasterSelect field to an AutocompleteFieldWidget.
I'm using AutocompleteFieldWidget from plone.formwidget.autocomplete and the MasterSelectField from plone.formwidget.MasterSelect. The slave field belonging to the MasterSelectField is also a MasterSelectField.
The autocomplete functions as it should (retrieving the values based on input), but the slave field's choices do not change. However, when its not set as an autocomplete, everything works as it should.
Edit:
In my buildout-cache, I looked at widget.py in plone.formwidget.masterselect and tried placing a print statement in getSlaves and that function wasn't getting called. I tried the render function and that wasn't getting called either. Then I placed a print statement in MasterSelectField and that was notgetting called. Setting the field to an Autocomplete widget removes any trace that its a Master Select field.
Edit: In the init.py file in plone.formwidget.masterselect, I placed a print statement in the init function of the MasterSelectField, and the slave widget does print, where as in getSlaves in widget.py it doesn't. This is the output I'm getting from printing in the init and what I should be getting in getSlaves:
({'action': 'vocabulary', 'masterID': 'form-widgets-IMyForm-master_field',
'control_param': 'master_value', 'name': 'IMyForm.slave_field',
'vocab_method': <class 'my.product.vocabulary.SlaveVocab'>},)
I have my interface:
from plone.directives import form
class IMyForm(model.Schema):
form.widget(master_field=AutocompleteFieldWidget)
master_field = MasterSelectField(
title=_(u'Master'),
slave_fields=({'name':'IMyForm.slave_field',
'action':'vocabulary',
'source':MySource,
'control_param':'master_value'
}),
required=True,
)
slave_field = MasterSelectField(title=_(u'Slave Field'),
source=SlaveVocab,
slave_fields=(....
)
required=False,
)
I have my source object for the master field:
class MySource(object):
implements(IQuerySource)
def __init__(self, context):
simple_terms = []
#Query portal catalog for unique indexes, and fill with simple terms
self.vocab = SimpleVocabulary(simple_terms)
def __contains__(self, term):
return self.vocab.__contains__(term)
def getTermByToken(self, token):
return self.getTermByToken(token)
def getTerm(self, value):
return self.getTerm(value)
def search(self, query_string):
return [term for term in self.vocab if query_string in term.title.lower()]
class MySourceBinder(object):
implements(IContextSourceBinder)
def __call__(self, context):
return MySource(context)
My slave field's source is:
class SlaveVocab(object):
grok.implements(IContextSourceBinder)
def __init__(self, **kw):
self.master_value = kw.get('master_value', None)
def __call__(self, context):
if self.master_value is None or self.master_value == "--NOVALUE--"
self.master_value = getattr(context,'master_field',None)
#Still nothing, return empty vocabulary
if self.master_value is None or self.master_value == '--NOVALUE--':
return SimpleVocabulary([])
terms = []
#If not null, building a simple vocabulary to return
return SimpleVocabulary(terms)
I did a print statement in call of the Slave Vocabulary and it was being called, but nothing was being passed in.
I also tried using another widget, ChosenFieldWidget. I get the same results in that it functions as it should, but the slave field's choices do not change. Is it possible to set a master select field to an autocomplete? If so, what am I doing wrong?
Also, I'm using Solgema.fullcalendar and the content type extends the IEventBasic behavior, so I don't have access to using my own form class I would've liked to have used since Solgema seems to render its own forms.
Edit:
I am using Plone 4.3

Cannot access the parameter of a Menu.param from a Lift Snippet

I'm trying to extract the parameter from a Lift Menu.param within a snippet so that I can use it to create a named Comet. However, I get a NullPointerException when I try to pass the parameter to the snippet using SnippetDisptach in my Boot.scala, as suggested here:
http://comments.gmane.org/gmane.comp.web.lift/44299
I've created the Menu item as follows:
object AnItemPage {
// create a parameterized page
def menu = Menu.param[Item]("Item", "Item",
s => fetchItem(s), item => item._id.toString) / "item"
private def fetchItem(s:String) : Box[Item] = synchronized {
ItemDAO.findById(ObjectId.massageToObjectId(s))
}
}
I've added the menu to SiteMap. I've also created a Snippet which I would like to pick up the Item parameter. (I'm using fmpwizard's InsertNamedComet library here):
class AddCometItemPage(boxedItem: Box[Item]) extends InsertNamedComet with DispatchSnippet{
val item : Item = boxedItem.openOr(null)
override lazy val name= "comet_item_" + item._id.toString
override lazy val cometClass= "UserItemCometActor"
def dispatch = null
}
My next step is to crate an instance of this class as demonstrated by David Pollak here:
http://comments.gmane.org/gmane.comp.web.lift/44299
This is what I have added to my Boot.scala:
LiftRules.snippetDispatch.append {
case "item_page" => new AddCometItemPage(AnItemPage.menu.currentValue)
}
My item.html references this snippet:
<div class="lift:item_page">
I get the following null pointer exception when I compile and run this:
Exception occurred while processing /item/5114eb4044ae953cf863b786
Message: java.lang.NullPointerException
net.liftweb.sitemap.Loc$class.siteMap(Loc.scala:147)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.siteMap(Menu.scala:170)
net.liftweb.sitemap.Loc$class.allParams(Loc.scala:123)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.allParams(Menu.scala:170)
net.liftweb.sitemap.Loc$class.net$liftweb$sitemap$Loc$$staticValue(Loc.scala:87)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.net$liftweb$sitemap$Loc$$staticValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.paramValue(Loc.scala:85)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.paramValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.currentValue(Loc.scala:114)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.currentValue(Menu.scala:170)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:107)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:106)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.common.Full.map(Box.scala:553)
net.liftweb.util.NamedPF$.applyBox(NamedPartialFunction.scala:97)
net.liftweb.http.LiftRules.snippet(LiftRules.scala:711)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.http.LiftSession.net$liftweb$http$LiftSession$$findSnippetInstance(LiftSession.scala:1505)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1670)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1669)
Has anybody any idea where I'm going wrong? I've not been able to find a lot of information on Menu.param.
Thank you very much for your help.
f
I have never tried what you are doing, so I am not sure the best way to accomplish it. The way you are using the Loc Param, you are extracting a variable from a URL pattern. In your case, http://server/item/ITEMID where ITEMID is the string representation of an Item, and which is the value that gets passed to the fetchItem function. The function call will not have a value if you just arbitrarily call it, and from what I can see you are requesting a value that is not initialized.
I would think there are two possible solutions. The first would be to use S.location instead of AnItemPage.menu.currentValue. It will return a Box[Loc[Any]] representing the Loc that is currently being accessed (with the parameters set). You can use that Loc to retrive currentValue and set your parameter.
The other option would be to instantiate the actor in your snippet. Something like this:
item.html
<div data-lift="AnItemPage">
<div id="mycomet"></div>
</div>
And then in your AnItemPage snippet, something like this:
class AnItemPage(item: Item) {
def render = "#mycomet" #> new AddCometItemPage(item).render
}
I haven't tested either of those, so they'll probably need some tweaking. Hopefully it will give you a general idea.

How to make a snippet in Lift - Scala

What I need to do is to put one tag into html page:
<lift:surround with="default" at="content">
<span class="lift:WorkingStatus.print"></span>
</lift:surround>
and to have a snippet class in appropriate snippet.* package:
class WorkingStatus {
def print():String={return "<table></table>";}
def speak = <span>Hello World</span>
def render = "* *" #> "Hello there in span"
}
function will generate some html table in string which will show up in html, like in def print() .
So, this is something rather trivial I cannot get to work in any way. I need a Scala class which could be incorporated wherever needed in html pages, like - lets say - with <jsp:include> and this class should be able to connect to outer web service, get list of some data and generate html which will then be inserted on desired places... complicated enough? :)
You have a wrong signature for the print method. Snippets' methods are NodeSeq => NodeSeq.
So the more verbose variant is
def print(xhtml:NodeSeq):NodeSeq={return <table></table>;} or you can use
def print = "*" #> <table></table>
if you will need a some kind of transformation, or just:
def print = <table></table>