blogdvelblogdvelhttps://blog.d-vel.com/home/-/blogs/rss2024-03-29T11:29:35Z2024-03-29T11:29:35ZCollegarsi a datasource esterni con Liferay 7.2+Marco Napolitanohttps://blog.d-vel.com/home/-/blogs/collegarsi-a-datasource-esterni-con-liferay-7-2-2022-02-17T14:48:36Z2022-02-17T13:00:00Z<p>Qualche anno fa avevo affrontato lo stesso tema relativamente alla neonata versione 7.0; per chi se lo fosse perso vi ripropongo il link: <a href="https://blog.d-vel.com/home/-/blogs/collegarsi-a-datasource-esterni-con-liferay-dxp">https://blog.d-vel.com/home/-/blogs/collegarsi-a-datasource-esterni-con-liferay-dxp</a>.</p> <p>Con la versione 7.1 ci fu un piccolissimo cambiamento (che potete trovare nei commenti del vecchio articolo) ma con l'avvento della versione 7.2 (e successive) tutto è cambiato! Di nuovo...</p> <p>Vediamo quindi come fare per configurare adeguatamente la connessione ad un database esterno con Liferay 7.2+ (metto il + perché sono ottimista e spero di non dover scrivere un altro articolo tra qualche anno).</p> <p>Resta però un punto fermo per tutte le versioni di Liferay 7+: tutte le entità definite all'interno del medesimo <code>service.xml</code> si collegano al medesimo datasource pertanto, per ogni datasource esterno a cui collegarsi, andrà definito uno specifico plugin di tipo <em>Service Builder</em>. Ma in fondo questa cosa rende il codice molto più modulare e manutenibile.</p> <p>La prima modifica da fare è quella di specificare per tutte le entità del <code>service.xml</code> l'attributo <code>data-source</code> andando ad inserire un qualsiasi valore fittizio, ad esempio:</p> <pre> <entity data-source="extDataSource" name="Foo"> [...] </entity> </pre> <p>Ovviamente, se necessario, andate a specificare anche gli attributi <code>table</code> e <code>column</code> (che mi aspetto sappiate cosa sono ed a cosa servono).</p> <p>Cosa scrivete nell'attributo <code>data-source</code> non ha molta importanza perché non viene usato da nessuna parte, serve solamente per comunicare a Liferay (ed al Service Builder) che <em>non bisogna usare</em> la connessione di default del portale.</p> <p>Dopodiché andiamo a definire nel <code>portal-ext.properties</code> i parametri di connessione al database esterno:</p> <pre id="yui_patched_v3_11_0_1_1645095480239_899" style="text-align: justify;"> jdbc.ext.driverClassName=<DRIVER_CLASS_NAME> jdbc.ext.url=<CONNECTION_URL> jdbc.ext.username=<USERNAME> jdbc.ext.password=<PASSWORD> jdbc.ext.connectionTestQuery=<CONNECTION_TEST_QUERY> <em>(se serve)</em></pre> <p>La cosa importante da ricordare è quella di utilizzare un prefisso univoco per ciascuna connessione esterna (nel caso ne aveste più di una); nel nostro caso il prefisso che abbiamo utilizzato è <code>jdbc.ext</code>.</p> <p>Bene, fino a questo punto le modifiche da fare sono esattamente uguali a quanto si faceva nelle versioni precedenti di Liferay. <span style="font-weight: inherit;">Quello che è cambiato arriva adesso!</span></p> <p>Infatti a partire dalla versione 7.2 è necessario implementare un'apposita classe di tipo <code>DataSourceProvider</code> per comunicare a Liferay quale connessione esterna utilizzare; questa classe andrà implementata indipendentemente dal fatto che il vostro <code>service.xml</code> abbia impostato <code>ds</code> o <code>spring</code> come meccanismo di injection.</p> <p>Non vi sto ad annoiare con la teoria perché probabilmente avrete già perso un sacco di tempo a cercare su Google, quindi mi limito a mostrarvi il codice della classe da implementare:</p> <pre> package it.dvel.liferay.service.persistence.impl.constants; import com.liferay.portal.kernel.dao.jdbc.DataSourceFactoryUtil; import com.liferay.portal.kernel.dao.jdbc.DataSourceProvider; import com.liferay.portal.kernel.util.PropsUtil; import javax.sql.DataSource; public class DataSourceProviderImpl implements DataSourceProvider { @Override public DataSource getDataSource() { try { return DataSourceFactoryUtil.initDataSource( PropsUtil.getProperties("jdbc.ext.", true)); } catch (Exception e) { throw new RuntimeException(e); } } }</pre> <p>Come vedete la classe non fa altro che restituire le property di connessione contraddistinte dal prefisso che avete scelto sopra (ossia <code>jdbc.ext.</code>); mi raccomando fate attenzione ad inserire anche il punto finale!</p> <p>Questa classe va messa nel plugin <code>-service</code> in un package qualunque; di solito uso il package <code>.service.persistence.impl.constants</code> perché esiste già e sono pigro. <img alt="smiley" height="20" src="https://blog.d-vel.com/html/js/editor/ckeditor/plugins/smiley/images/regular_smile.gif" title="smiley" width="20" /></p> <p>A questo punto manca <em>l'ultimo passaggio</em>!</p> <p>Sempre nel plugin <code>-service</code> create un file di testo nella cartella <code>src/main/resources/META-INF/services</code> e <em>chiamatelo esattamente</em> <code>com.liferay.portal.kernel.dao.jdbc.DataSourceProvider</code> ossia come l'interfaccia che avete implementato nella classe vista sopra.</p> <p>Il contenuto di questo file di testo sarà un'unica riga costituita dal <em>nome completo di package</em> della classe che avete implementato, ossia:</p> <pre> it.dvel.liferay.service.persistence.impl.constants.DataSourceProviderImpl </pre> <p>Ora salvate tutti i file, lanciate il task <code>build-service</code> del Service Builde ed una volta terminato deployate i plugin.</p> <p>Se avete seguito tutte le istruzioni alla lettera, vedrete nei log di Tomcat il messaggio relativo alla connessione al database esterno.</p> <p>Enjoy!</p>Marco Napolitano2022-02-17T13:00:00ZImplementare una factory in un container OSGiJader Jed Franciahttps://blog.d-vel.com/home/-/blogs/implementare-una-factory-in-osgi2021-03-12T12:31:46Z2021-02-18T09:00:00Z<p>Ciao a tutti!</p> <p>L'articolo di oggi verte su uno dei pattern applicativi più noti (nella mia personale classifica, nei colloqui che facciamo è mediamente il secondo che viene detto dopo il Singleton): il factory pattern!</p> <p>Non farò una dissertazione sul pattern in se, perché credo che Google possa fornirvi tutti gli articoli dell'universo (fatti anche molto meglio di come li farei io) su di esso; tuttavia mi piaceva l'idea di darvi uno spunto per una sua possibile implementazione all'interno di un container OSGi.</p> <h1>Il problema</h1> <p>Come sempre, però, vi racconto il caso d'uso che mi ha portato a questa implementazione, così che anche voi possiate avere un po' di contesto.</p> <p>Il caso d'uso di questa volta, incredibile a dirsi, non arriva da uno dei miei animali mitologici, bensì da un <strong>bisogno personale</strong>!</p> <p>Mi spiego: come spesso accade, mi trovo a fare un po' di audit sul codice che ho scritto e mi rendo conto che, onestamente, un po' di <strong>refactor</strong> potrebbe migliorarne quanto meno <strong>la leggibilità</strong> se non, come in questo caso, <strong>la manutenibilità</strong>.</p> <p>Nello specifico, in un metodo, avevo il classico "if / else / if" e, dopo il terzo "if", mi sono trovato ad aggiungerne un quarto.</p> <p>Chiaramente tutti avete già storto il naso: chi non avrebbe implementato un factory già al terzo "if" alzi la mano! :)</p> <p>Io invece, siccome sono mediamente un po' più indietro della media :), ho pensato di implementarlo al quarto.</p> <p>Allora mi sono messo a ragionare (in realtà a scrivere codice mentre lo facevo.. :)), per implementarlo.</p> <p>Siccome però ero all'interno del container OSGi, oggi vero cuore pulsante di Liferay, mi sono trovato davanti a un problema: come implemento una <code>factory</code> che mi ritorna <code>Service</code> OSGi? Eggià, perché il mio caso non era banalmente sostituire la cascata di "if / else" con un factory, era anche quello di permettere agli oggetti ritornati dalla factory stessa di beneficiare della dependency injection offerta dal container.</p> <p>Allora mi sono messo di buona lena e, con un po' di buzzo buono, mi sono lanciato a fare test, leggere specifiche e, più in generale, a cercare una soluzione che risolvesse il mio problema.</p> <p>Dopo circa due ore di mal di testa (sfido chiunque a non impazzire leggendo le specifiche di OSGi riga per riga alla ricerca di un suggerimento pratico..) ho avuto un'illuminazione!</p> <p>"Ma sono un genio", ho pensato, "il service tracker è di fatto una factory (molto alla lontana ma seguite il ragionamento) e quello che ritorna, i Service OSGi, sono di fatto gli elementi che servono a me"!</p> <p>Da questa riflessione, che vi giuro mi ha spaccato in due come una mela che cade su una motosega impazzita :), ho dedotto che il <code>Factory</code> pattern in OSGi è morto, perché, di fatto, sostituito dal <code>Service</code> pattern!</p> <h2>Il Factory pattern è morto: viva il Factory pattern!</h2> <p>Ok, direte voi: questo è ovvio. Lo so, lo avevo anche premesso: non sono un fulmine di guerra! ;)</p> <p>Però mi sono detto: a questo punto come risolvo il problema dello "switch" che dovrei fare, per discriminare <em>quale</em> tra gli oggetti che sono ritornati devo usare?</p> <p>La risposta è stata ovvia: <code>property</code> di un <code>Component</code>! Posso fare uno switch in base ad una property e quindi, con un filtro, avere quello che mi serve!</p> <p>Anche qui, direte voi, ovvio. Siete un pubblico difficile quest'oggi! :)</p> <p>Ancora una volta, però, non mi sono arreso e ho cominciato a cercare una soluzione che mi mettesse felice e mi permettesse di recuperare, dato il filtro, il Service a me più congeniale.</p> <h1>La soluzione</h1> <p>Ed ecco la mia possibile soluzione, con la speranza che possa tornare utile anche a voi!</p> <div class="portlet-msg-alert">Spoiler alert: quello che segue non è LA soluzione, bensì, "la migliore soluzione che ho trovato con i constraint di tempo e budget che avevo". Don't blame me for that! :)</div> <p>Ometto le parti di codice "ovvie", quelle ad esempio dove accedo al <code>ServiceTracker</code>, ma mi concentro sul metodo che, di fatto, rappresenta la mia factory!</p> <pre> <code>private TaskExecutor getTaskExecutor(Element element) { Class[] interfaces = element.getClass().getInterfaces(); for (Class interfaceObj : interfaces) { List<taskexecutor> executors = serviceProvider.getServices(TaskExecutor.class, "task.executor.type", interfaceObj.getSimpleName()); if (Validator.isNotNull(executors) || executors.size() > 0) return executors.get(0); } return new TaskExecutor() { @Override public boolean executeTask(ActivityTask task, ModelitDataRecord dataRecord, ModelitBPMEngineLocalService service, ServiceContext serviceContext) throws Exception { return false; } }; } </taskexecutor></code></pre> <p>Come potete vedere l'idea è semplice: i miei <code>Component</code> hanno una property (<code>task.executor.type</code>) sulla quale mi baso per fare il case.</p> <p>Questa property, nella mia convenzione, è mappata su un'interfaccia applicativa che gli oggetti sui quali faccio lo switch implementano. Ora, mi è ben chiaro che questa soluzione non è assolutamente scalabile, è forse un po' troppo artigianale e magari non è elegantissima; so anche che se uno dei miei oggetti implementa più interfacce (possibile), la cosa non sta in piedi ma vi garantisco che nel mio modello tutte queste cose sono superabili! :)</p> <p>Come vedete, però, l'obiettivo l'ho raggiunto: passo alla mia (finta) <code>factory</code> l'oggetto sul quale fare il case e a runtime, sfruttando la <code>reflection</code>, recupero l'oggetto a me più congeniale.</p> <p>Concludo con un dettaglio: nel mio caso è assolutamente plausibile che non ci sia un oggetto di ritorno per ogni oggetto in input, quindi ho implementato come default un <code>DummyObject</code> che mi consente di mantenere consistente il client e mi previene dalle <code>NullPointerException</code>!</p> <p>Bene: anche per oggi vi ho fatto perdere un po' di tempo ragionando su pattern e possibili implementazioni su container OSGi; come sempre, nel caso abbiate dubbi / domande, potete utilizzare il box dei commenti!</p> <p>A presto e, buon (<code>Factory</code>)<code>Service</code> pattern a tutti! :)</p>Jader Jed Francia2021-02-18T09:00:00ZCome usare i typeSettings sui Layout!Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/come-usare-i-typesettings-sui-layout-2020-12-15T09:05:51Z2020-12-15T09:05:00Z<p> </p> <p>Ciao a tutti!</p> <p>Tutti sapete che la tabella <code>Layout</code> contiene le pagine che vengono generate dal portale; forse non tutti però conoscete i <code>typeSettings</code>, un campo della tabella <code>Layout</code> all'interno del quale, tipicamente, il portale scrive l'associazione tra gli spazi dei singoli <code>layouttpl</code> e le relative portlet.</p> <p>Ma come possiamo usarli "a nostro piacimento"?</p> <p>Questa domanda, come sempre, mi è stata fatta da un gruppo di ragazzi che sta facendo un ottimo lavoro mentre fanno il porting dalla 6.2 EE alle 7.2 EE di una applicazione che non hanno scritta loro ma che hanno ereditato.</p> <p>(N.d.J: Della serie: doppio carpiato rovesciato, ma bendati e legati dopo aver girato in tondo per 5 minuti e poi fatti saltare da 35 metri d'altezza... Per darvi un'idea! :))</p> <p>E la domanda, più che lecita, mi è stata rivolta perché chi ha codificato prima di loro l'applicativo, ha pensato bene di utilizzare i <code>typeSettings</code> anche per salvare caratteristiche delle pagine.</p> <p>Posto che noi in D'vel è una vita che facciamo queste robe (ma ce la siamo sempre spicciata facile usando i Custom Attribute :)), m'intrigava la soluzione che avevano realizzato e quindi ho lavorato con loro per riuscire a fare il porting del codice sulla 7.2.</p> <h1>Il caso funzionale</h1> <p>Il caso funzionale che avevano mappato era semplice: in alcune pagine è presente una portlet (scusate: widget, siamo sulla 7.. :D); questo widget però deve ereditare alcuni parametri per essere configurato, così chi ha codificato l'applicativo ha pensato bene di salvare nei <code>typeSettings</code> questi parametri.</p> <p>(N.d.J: faccio notare che la portlet poteva essere semplicemente configurata sulle singole pagine, senza stare tanto ad impazzire; ma non chiedetemi perché è stata scelta questa strada: se la vita fosse semplice a noi non ci cercherebbe nessuno, quindi.. ;D).</p> <h1>La vecchia implementazione</h1> <p>Per riuscire a fare questa implementazione, i vecchi developer avevano proceduto in questo modo:</p> <ul> <li>avevano fatto un bell'hook sulla portlet che gestiva il back end della gestione delle pagine;</li> <li>avevano fatto una bella JSP che si agganciava al <code>form-navigator</code> della gestione delle pagine;</li> <li>accedendo alla nuova voce all'interno del <code>form-navigator</code> avevano messo la loro bella JSP;</li> <li>al submit i dati venivano salvati trasparentemente nella <code>Layout</code>, così che il gioco fosse fatto!</li> </ul> <p>Analizzando nel dettaglio la loro implementazione, in effetti posso anche riconoscere che è sicuramente più elegante e figa della nostra:</p> <ul> <li>il cliente non accede ai "Campi personalizzati" ma al back end standard di prodotto;</li> <li>all'interno delle voci di configurazione della pagina c'era la loro voce;</li> <li>accedendo alla loro voce c'era una form "impaginata a modo" (e non autogenerata come quella dei Custom Fields) che non era in effetti niente male..</li> </ul> <p>Cavolo, allora la sfida si faceva interessante! :)</p> <h1>La nuova implementazione</h1> <p>La prima cosa che c'era da fare, quindi, era sostituire l'hook che, sulla 6.2, si agganciava con una JSP deployata sul portale, al <code>form-navigator</code> presente all'interno della gestione delle pagine.</p> <p>Nella 7, il <code>form-navigator</code> ovviamente si è evoluto ed è quindi diventato necessario sviluppare due <code>Components</code> per poterlo utilizzare / per potercisi collegare in maniera trasparente.</p> <p>Il primo serve per creare la <strong>categoria</strong> all'interno del menù di navigazione del portale; il secondo per creare le singole <strong>sezioni</strong> che ci sono all'interno di questo menu.</p> <p>Quindi abbiamo proceduto in questo modo; prima abbiamo creato la categoria:</p> <pre> <code> package it.dvel.playground.web.layout; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorCategory; import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorConstants; import java.util.Locale; import org.osgi.service.component.annotations.Component; @Component( immediate = true, property = "form.navigator.category.order:Integer=10", service = FormNavigatorCategory.class) public class CustomLayoutFormNavigatorCategory implements FormNavigatorCategory { @Override public String getFormNavigatorId() { return FormNavigatorConstants.FORM_NAVIGATOR_ID_LAYOUT; } @Override public String getKey() { // Io ho fatto una PoC; voi fate i bravi e usate una COSTANTE!! :) return "custom-category"; } @Override public String getLabel(Locale locale) { return LanguageUtil.get(locale, "custom-category"); } } </code></pre> <p>Fatto questo, abbiamo creato il pezzo di pagina che ci interessava.. O meglio: la entry che si sarebbe agganciata alla nostra category custom:</p> <pre> <code> package it.dvel.playground.web.layout; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.model.Layout; import com.liferay.portal.kernel.servlet.taglib.ui.BaseJSPFormNavigatorEntry; import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorConstants; import com.liferay.portal.kernel.servlet.taglib.ui.FormNavigatorEntry; import java.util.Locale; import javax.servlet.ServletContext; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @Component( property = "form.navigator.entry.order:Integer=100", service = FormNavigatorEntry.class) public class CustomLayoutFormNavigatorEntry extends BaseJSPFormNavigatorEntry<layout> implements FormNavigatorEntry<layout> { @Override protected String getJspPath() { return "/html/admin_layout/my_custom_fields_to_manage.jsp"; } @Override public String getCategoryKey() { // Le costanti ragazzi: voi usate le costanti! ;) return "custom-category"; } @Override public String getFormNavigatorId() { return FormNavigatorConstants.FORM_NAVIGATOR_ID_LAYOUT; } @Override public String getKey() { // Non mi stancherò mai di ripetermi: usate le costanti! ;) return "custom-entry"; } @Override public String getLabel(Locale locale) { return LanguageUtil.get(locale, getKey()); } @Override @Reference( target = "(osgi.web.symbolicname=my-poc-module-web)", unbind = "-") public void setServletContext(ServletContext servletContext) { // Questo setter è strategico: permette al container di recuperare // la JSP che abbiamo indicato sopra direttamente dal nostro bundle // e non a raglio da chissà quale pacchetto! // Ricordatevi che questa roba sarà eseguita dal bundle standard della // gestione delle pagine! super.setServletContext(servletContext); } } </layout></layout></code></pre> <p>E anche questa è fatta!</p> <p>Ora non rimaneva che sistemare la nostra JSP, all'interno del nostro bundle, e far gestire tutto al componente standard di portale.</p> <p>Ed ecco qui la JSP:</p> <pre> <%@ include file="/META-INF/resources/html/init.jsp"%><code> <% // Recupero il plid del Layout visualizzato dalla request Long selPlid = ParamUtil.getLong(renderRequest,"selPlid"); // Recupero il Layout usando il plid Layout selLayout = LayoutLocalServiceUtil.getLayout(selPlid); UnicodeProperties layoutTypeSettings = null; if (selLayout != null) { // Se il Layout non è nullo, recupero le typeSettings layoutTypeSettings = selLayout.getTypeSettingsProperties(); } %> <liferay-ui:error-marker key="error-section" value="my-custom-fields-to-manage-error-message" /> <aui:model-context bean="<%= selLayout %>" model="<%= Layout.class %>" /> <h3><liferay-ui:message key="my-custom-fields-to-manage-title" /></h3> </code><aui:fieldset cssClass="lfr-portrait-editor"><code><aui:fieldset cssclass="lfr-portrait-editor"> <% String riskType = GetterUtil.getString(layoutTypeSettings.getProperty("risk-type")); %> <aui:select label="risk-type" <strong>name="TypeSettingsProperties--risk-type--"</strong> showemptyoption="<%= true %>"> <aui:option label="type-car" selected="<%= "CAR".equals(riskType) %>" value="<%=Constants.CAR%>"/> <aui:option label="type-motorbike" selected="<%= "MOTORBIKE".equals(riskType)%>" value="<%=Constants.MOTORBIKE%>"/> <aui:option label="type-easy" selected="<%= "EASY".equals(riskType) %>" value="<%= Constants.EASY %>"/> <aui:option label="type-quote" selected="<%= "QUOTE".equals(riskType) %>" value="<%= Constants.VERTIQUOTE %>"/> <aui:option label="type-home" selected="<%= "HOME".equals(riskType) %>" value="<%= Constants.HOME %>"/> <aui:option label="type-other-vehicle" selected="<%= "OTHER_VEHICLE".equals(riskType) %>" value="<%=Constants.OTHER_VEHICLE%>"/> <aui:option label="type-ivass" selected="<%= "IVASS".equals(riskType) %>" value="<%= Constants.IVASS %>"/> </aui:select> </aui:fieldset> </aui:fieldset></code></pre> <p>Ed eccolo qui, l'utimo tassello del puzzle! :)</p> <p>Una nota importante: come potete vedere, nel nome della select è stato inserito:</p> <pre> <code><aui:fieldset cssclass="lfr-portrait-editor"><strong>TypeSettingsProperties--risk-type--</strong></aui:fieldset></code></pre> <p>che i più attenti di voi avranno già riconosciuto come un meccanismo "automatico" che viene usato dal portale per leggere e salvare arbitrari valori che arrivano dal web (di fatto è la stessa convenzione che si utilizza sul salvataggio delle configurazioni!).</p> <p>Detto questo, ovviamente, deploy, navigazione, test e... Funziona! ;)</p> <p>A questo punto, anche voi (come noi :D), adesso potete utilizzare le typeSettings di pagina come un modo più elegante e furbo per far configurare parametri al Cliente.</p> <p>.. Sempre che non abbiate una fretta del diavolo e i Custom Attribute non vi sembrino molto più smart e semplici da utilizzare!!</p> <p>Direi che anche per oggi è tutto: se avete dubbi o domande, come sempre, sono a vostra disposizione; scriveteci nei commenti e fateci sapere che ne pensate!</p> <p>Buona giornata a tutti! ;)</p>Jader Jed Francia2020-12-15T09:05:00ZCaricamento via AJAX senza impazzimenti!Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/caricamento-via-ajax-senza-impazzimenti-2020-12-13T22:05:46Z2020-12-13T08:45:00Z<p> </p> <p>Ciao a tutti!</p> <p>Il problema di oggi è una roba che molti di voi faranno da una vita; siccome però mi sono trovato su un progetto a farlo, ci ho messo un po' ma sono arrivato anch'io! :)</p> <p>Il problema è molto semplice: abbiamo un componente sul frontend che triggera il caricamento via AJAX di una porzione di HTML che, tuttavia, contiene del JS e che quindi dev'essere parsato.</p> <p>Ovviamente, poi, se quando invio la form ci sono degli errori di validazione, quando torno in pagina dovrei:</p> <ul> <li>trovare caricato il frammento di HTML;</li> <li>trovare <em>valorizzato</em> il frammento di HTML con i valori selezionati.</li> </ul> <p>Nel mio caso funzionale, l'obiettivo era che, dopo aver utilizzato l'inline search, dall'id dell'oggetto selezionato dovevo caricare tutte le entità figlie ad esso collegate e:</p> <ul> <li>permettere di selezionarli singolarmente;</li> <li>permettere di selezionarli tutti in un click.</li> </ul> <p>Per non reinventare la ruota, l'idea che mi è venuta era quella di utilizzare il <code>SearchContainer</code> insieme alla funzionalità del <code>RowChecker</code>, così che mi venisse gratis tutta la parte di codice di impaginazione dei figli ma anche, appunto, la possibilità di selezioare tutti o alcuni dei record visualizzati.</p> <p>Però sono in una JSP già caricata.. E come faccio a caricare questa roba, che, siccome parliamo di <code>SearchContainer</code>, dev'essere renderizzata usando le taglib di portale?</p> <p>Beh, la risposta è semplice: AJAX!</p> <p>Ok, ma.. Come? :)</p> <p>Cercando un po' sui progetti che abbiamo fatto, mi sono ricordato di una roba che aveva fatto Paolo Gambetti e che avevo molto elegante; quindi ho recuperato tutto e messo tutto insieme!</p> <p>Vediamo ora un po' di codice..</p> <h1>Caricamento asincrono via AJAX dell'HTML (con relativo parsing)</h1> <p>La prima cosa che ho fatto, è stata quella di mappare in una funzione JS di pagina, la logica di caricamento e popolamento del componente. Questo l'ho fatto ovviamente perché devo gestire due casi:</p> <ol> <li>quando si carica la pagina la prima volta e, al trigger sul front end, devo caricare il frammento HTML;</li> <li>quando si <strong>ricarica</strong> la pagina e il componente deve riapparire popolato!</li> </ol> <p>La funzione JS è molto semplice:</p> <pre> <code> Liferay.provide(window, '<portlet:namespace/>loadSalesPoints', function(customerId) { // Questa chiamata serve perché una volta che ho caricato il componente, // questo viene registrato e al caricamento successivo ho un errore; // ma se lo rimuovo funziona tutto! :) Liferay.destroyComponent('<portlet:namespace/>salespointsSearchContainer'); customerIdField.val(customerId); var portletURL = Liferay.PortletURL.createRenderURL(); portletURL.setPortletId('<%=PortletKeys.CALENDAR %>'); portletURL.setPlid(<%= plid %>); portletURL.setWindowState('<%=LiferayWindowState.EXCLUSIVE.toString() %>'); portletURL.setParameter('customerId', customerId); portletURL.setParameter('mvcPath', '/html/calendar/excel/planCalendar/show_sales_points.jsp'); pvContainerField.plug(A.Plugin.IO, { failureMessage: 'In elaborazione...', parseContent: true, showLoading: true, after: { success: function(event) { <c:if test="<%= !SessionErrors.isEmpty(renderRequest) %>"><c:if test="<%= !SessionErrors.isEmpty(renderRequest) %>"> var salesPointId = "<%= ParamUtil.getString(renderRequest, "salesPointIds")%>"; var salesPointArray = salesPointId.split(','); // Recupero tutti i field con name "<portlet:namespace/>rowIds" // leggo i loro valori e se corrispondono setto il flag checked A.all('input[name=<portlet:namespace/>rowIds]').each(function (field) { for (var i = 0; i < salesPointArray.length; i++) { var arrValue = salesPointArray[i]; if (field.val() == arrValue) { field.setAttribute('checked', true); } } }); </c:if> </c:if> } }, uri: portletURL.toString(), where: 'replace' }); pvContainerField.io.start(); }, ['aui-base', 'aui-io-plugin-deprecated', 'liferay-portlet-url']); </code></pre> <p>Come sicuramente avrete notato, ci sono questi accorgimenti:</p> <ul> <li>nella definizione della funzione, uso il <code>namespace</code> per renderla univoca: questo viene fatto così se finsice in pagina più volte almeno viene sendboxata;</li> <li>subito dopo il caricamento via AJAX del frammento HTML (<code>after: success: {}</code>) uso un <code><cite><c:if/></cite></code> per capire se sono tornato in pagina a causa di un errore oppure se sono in creazione; questo ovviamente mi serve per <strong>ripopolare</strong> il componente con i valori corretti;</li> <li>la magia del caricamento avviene in automatico quando chiamo la funzione: è stata bindato sul componente <code>A.Plugin.IO</code>, che permette (anche se deprecato) il caricamento via AJAX dell'HTML che mi serve;</li> <li>la magia del parsing mi viene offerta gratis sempre da A.Plugin.IO: grazie all'attributo <code>parseContent: true</code> viene attivato l'eval del JS nella pagina (figo!);</li> <li>grazie alla direttiva <code>where: replace</code>, l'HTML che sarà servito lato server farà la sostituzione del mio markup.</li> </ul> <p>Direi che non c'è bisogno di molte altre spiegazioni; il codice è abbastanza semplice ma, se avete dubbi, lasciateli nei commenti che rispondiamo! ;)</p> <h1>La JSP che viene caricata</h1> <p>Beh, questa è proprio "semplice":</p> <ul> <li>riceve via GET il parametro dell'id del record padre;</li> <li>dal db recupera la lista di figli (escludo la paginazione, ma qui <a href="#paginazione-si-paginazione-no">c'è un dettaglio che spiegherò più sotto</a>);</li> <li>uso il <code>SearchContainer</code> e abilito il <code>RowChecker</code>.</li> </ul> <p>Questo è il codice:</p> <pre> <code><%@ page import="com.liferay.portal.kernel.dao.search.RowChecker" %> <%@ include file="/META-INF/resources/html/init.jsp"%> <% long customerId = ParamUtil.getLong(request, "customerId"); %> <liferay-ui:search-container delta="200" deltaconfigurable="false" emptyresultsmessage="no-entries-were-found" <strong> rowChecker="<%=new RowChecker(renderResponse) %>"</strong> total="<%= SalespointLocalServiceUtil.countByG_C(scopeGroupId, customerId) %>"> <liferay-ui:search-container-results results="<%= SalespointLocalServiceUtil.findByG_C(scopeGroupId, customerId)%>"/> <liferay-ui:search-container-row classname="it.dvel.example.project.calendar.model.Salespoint" keyproperty="salespointId" modelvar="salesPoint"> <liferay-ui:search-container-column-text> <%= SalespointAddressFormatter.format(salesPoint)%> </liferay-ui:search-container-column-text> </liferay-ui:search-container-row> <liferay-ui:search-iterator paginate="false"/> </liferay-ui:search-container></code></pre> <p>Qui l'unica cosa degna di nota è l'abilitazione del <code>RowChecker</code> che ho evidenziato sul codice qui sopra!</p> <p>Ovviamente tutto questo funziona quando, da qualche parte nel mio JS in pagina, io chiamo la funzione che abbiamo mappato in precedenza:</p> <p><code><portlet:namespace/>loadSalesPoints(result.id);</code></p> <p>E questo è tutto quello che dovrebbe servirvi per far funzionare il giro come indicato qui sopra! :)</p> <p>C'è ancora un punto, però, che secondo me vale la pena segnalare!</p> <h1><a name="paginazione-si-paginazione-no">Paginazione si, paginazione no: attenzione però al <em>numero</em> di risultati!</a></h1> <p>Io nel mio caso sono stato fortunato; la numerica dei record figli è sempre molto bassa (al massimo 20 righe); perché vi dico questo, però?</p> <p>Beh, perché forse non lo sapete ma il <code>SearchContainer</code> ha una limitazione: non può caricare più di 200 record in una finestra (mi pare che fossero tipo 1.000 nella 6.2 ma nella 7 sono stati abbassati a 200).. Questo, ovviamente, per performance e buona gestione della memoria.</p> <p>Quindi ricordate: quando usate il <code>SearchContainer</code> e volete presentare in una botta sola tutti i record, fare in modo che il vostro numero massimo sia minore o uguale al limite che vi ho esposto sopra!</p> <p>Alla prossima! ;)</p>Jader Jed Francia2020-12-13T08:45:00ZLiferay SDK 6.2 con IntelliJ (e build in ANT)Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/liferay-sdk-6-2-con-intellij-e-build-in-ant-2020-11-20T10:38:42Z2020-11-20T07:43:00Z<p>Buongiorno a tutti e ben ritrovati!</p> <p>Questo è proprio un periodo felice: riesco a scrivere un sacco di non-post (come quello di ieri ma anche come quello che sto scrivendo ora.. :D) che affrontano tematiche che.. Interessano praticamente solo me! ;D</p> <p>In realtà quello di oggi è un topic che mi da un po' da fare da sempre; ho sempre avuto il pallino di abbandonare Eclipse (o il Liferay Developer Studio) in funzione di una IDE che fosse un po' più robusta, stabile, funzionante, chi-più-ne-ha-più-ne-metta... :D</p> <p>Scherzi a parte, senza nulla togliere al Liferay Developer Studio 2.2.2 (che ad oggi l'Ing. Napolitano lista ancora nella sua personale top list delle versioni del LDS -di cui è un attento e preciso catalogatore seriale.. :D-), avevo da un po' cominciato a guardare al tema ma senza successo.</p> <h1>L'antefatto...</h1> <p>Poi è successo un fatto.. Nel weekend ho fatto upgrade del sistema operativo <strong>senza fare backup</strong> (prima volta in 20 anni.. :/).</p> <p>Beh, Big Sur (ho un mac..) ha un piccolo glitch per cui non viene più impostata correttamente la <code>JAVA_HOME</code>.</p> <p>Poco male, direte voi, mica tu Jader sei uno che sviluppa.. :D</p> <p>... Vero!</p> <p>Però, però, però, c'è un però.. :D</p> <p>... Questa settimana stavo proprio tenendo un corso guardacaso sulla 6.2 di LR.</p> <p>E il fatto che non mi setti più la <code>JAVA_HOME</code> (anche se a manazza sembra averla correttamente settata..), <strong>il Liferay Developer Studio 2.2.2. NON mi parte più! </strong></p> <p>[N.d.J.: Appunto che ho correttamente configurato l'init di Eclipse impostando a manazza l'argomento per la corretta JVM, ma niente. Parte ma si frizza sullo splash screen e sulla selezione del workspace..]</p> <h1>L'esigenza..</h1> <p>Quindi quello che fino a sabato mattina era solo un vezzo, ora è diventato una triste necessità! ;)</p> <p>Per terminare il corso non ho avuto grossi problemi: ho riesumato il mac che avevo dismesso durante il lockdown in favore del nuovo e quindi il corso è terminato correttamente.</p> <p>Però ci sono alcuni progetti sui quali abbiamo della maintenance attiva per i quali, purtroppo o per fortuna :), dobbiamo fare manutenzione.</p> <p>[N.d.J.: Dobbiamo.. Devo: perché né l'Inge, né gli altri colleghi ne vogliono sapere.. :D]</p> <h1>La soluzione</h1> <p>Quindi questa mattina, di buon'ora (come sono solito fare da quando sono in <em>smart working</em> :)), mi sono messo d'impegno per far funzionare il LRSDK62 su IntelliJ.. </p> <div class="portlet-msg-info">Spoiler: la soluzione che segue è ovvia, lo so, talmente ovvia che i più, tra voi, sicuramente la bolleranno come "minchiata" :). La scrivo comunque, però, perché un giorno mi servirà e allora non dovrò rifare tutto il giro che ho fatto questa mattina! :)</div> <p>Ragionandoci un po' su ho avuto un'intuizione: il DevStudio non fa altro che gestire dinamicamente il classpath e poi via ANT eseguire dei task.</p> <p>Se sistemo a manazza il classpath e faccio in modo che ANT buildi, vinco facile.. :)</p> <p>Quindi:</p> <ul> <li>ho aperto in IntelliJ <strong>LE SINGOLE PORTLET</strong> contenute all'interno del SDK; <ul> <li>Quindi i progetti contenuti nelle varie folder del SDK (portlets, layouttpl, hooks, etc..);</li> </ul> </li> <li>ho configurato il classpath (more on this later...) delle singole portlet impostando: <ul> <li>il classpath globale con tutte le dipendenze per far compilare i miei portlet;</li> <li>il classpath dei singoli moduli puntando alla loro <code>WEB-INF/lib</code> interna (quando necessario);</li> </ul> </li> <li>ho configurato ANT (la versione corretta) e l'ho fatto funzionare (solo da command line, sorry...)</li> </ul> <h1>Dettagli tecnici</h1> <p>Questo è lo screenshot di come ho configurato il classpath globale:</p> <p><img alt="" src="https://blog.d-vel.com/documents/185441/186313/libraries-global.png/60728166-6a8b-46c7-a838-15a963f8e7d8?t=1605864947532" style="height: 538px; width: 649px;" /></p> <p>Come sicuramente avrete notato bisogna aggiungere (per praticità / velocità):</p> <ul> <li>tutti i JAR contenuti nella <code>lib</code> del Tomcat bundle Liferay;</li> <li>tutti i JAR contenuti nella <code>lib/ext</code> del Tomcat bundle Liferay;</li> <li>questi <strong>singoli</strong> JAR, presi dalla <code><tomcat home>/webapps/ROOT/WEB-INF/lib</code>: <ul> <li><code>commons-logging.jar</code></li> <li><code>jstl-api.jar</code></li> <li><code>jstl-impl.jar</code></li> <li><code>log4j.jar</code></li> <li><code>util-bridges.jar</code></li> <li><code>util-java.jar</code></li> <li><code>util-taglib.jar</code></li> </ul> </li> </ul> <p>Fatto questo gli artefatti vi compileranno già; rimane da sistemare ANT.</p> <p>Per farlo funzionare, mi sono scaricato la versione <code>apache-ant-1.9.15</code> e, a mano, lancio i singoli task <strong>da dentro alle folder dei singoli plugin. </strong>Questo approccio funziona: compila e fa deploy correttamente!</p> <h1>Conclusioni</h1> <p>Sicuramente starete dicendo peste e corna di questo post ;), mi sembra quasi di sentirvi! :)</p> <p>Però dovete ammettere che di articoli che spiegano questa cosa non ce ne sono proprio su internet... Forse perché, come dicevo all'inizio, forse è un problema soltanto mio.. :D</p> <p>Aggiungo che tutto quanto sopra funziona perché io ho già un SDK che è correttamente configurato: intendo che al suo interno è già presente, parametrizzato correttamente dal Liferay Developer Studio, il file <code>build.jed.properties</code> (che è il file che permette ad ANT di funzionare correttamente).</p> <p>Se così non fosse, nel senso che state iniziando un progetto nuovo sulla 6.2, beh, il mio consiglio è <strong>FERMARVI</strong> e cominciare il vostro nuovo progetto su LR7.x perché, e sono serio, la vostra vita sarà molto più semplice e divertente! ;)</p> <p>In ultima analisi, è ovvio che questo paradigma (LR62 + IntelliJ) è un palliativo: quello che si perde nel non usare il Liferay Developer Studio è tanta roba (snippet, pannellini di configurazione, wizard.. Tanta roba davvero!).</p> <p>È vero anche però, per contro, che il tempo che guadagnerete lavorando con questa configurazione sarà mostruosamente più alto del tempo che Eclipse -io parlo per me, magari la vostra esperienza è diversa..- vi fa perdere di solito.. :)</p> <p>Detto questo, sperando di avervi aiutato a migliorare un po' la vostra performance lavorativa (o aver risolto il problema che <strong>BUG</strong> Sur ha introdotto.. :)), vi lascio e vi do appuntamento alla prossima! ;)</p> <p>Divertitevi e buon IntelliJ a tutti (anche con LR62)!! ;)</p>Jader Jed Francia2020-11-20T07:43:00ZFatal: could not read username for <repo> device not configuredJader Jed Franciahttps://blog.d-vel.com/home/-/blogs/fatal-could-not-read-username-for-repo-device-not-configured2020-11-19T07:24:37Z2020-11-19T07:15:00Z<p>Buongiorno a tutti!</p> <p>Quello di oggi non è un "vero" post (come quelli che siete abituati a leggere su questo blog ;)), ma è più un <em>post di servizio</em>..</p> <p>Questa mattina, giocando con <a href="https://www.sourcetreeapp.com/" target="_blank">SourceTree</a>, ho fatto una hotfix su un ramo di produzione per un cliente e, all'atto di chiudere la hotfix mi sono imbattuto nel messaggio che vedete come titolo!</p> <p>Scrivo qui la soluzione, così la prossima volta che mi succederà non dovrò cercare su tutto il web il post che ho messo anche nelle risorse! :) </p> <p>La soluzione è semplice: bisogna dire a SourceTree di utilizzare il git di sistema e non il suo interno.</p> <p>Come fare? Easy:</p> <p><cite><span style="color: rgb(36, 39, 41); font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 13px;">If anybody using SourceTree, similar error was fixed by going to Preferences -> tab Git -> section Git version -> button Use System Git, and select from the dialog 'git'. It appears that after an update it forgot where it's command line tools are</span></cite></p> <p><cite>E, come aggiunge un altro utente nello stesso thread:</cite></p> <p><cite><span style="color: rgb(36, 39, 41); font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 13px;">Just remember to restart SourceTree then it works</span></cite></p> <p>Per oggi è tutto; prometto che non appena avrò un minuto vi scriverò un post succoso degno dell'Ing. Napolitano, promesso! ;)</p> <p>Alla prossima!</p> <h4>Source:</h4> <ul> <li><a href="https://stackoverflow.com/questions/40274484/fatal-could-not-read-username-for-https-github-com-device-not-configured" target="_blank">Stack Overflow</a>, dove ho trovato la risposta!</li> <li><a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow" target="_blank">Git Flow</a> spiegato bene</li> </ul>Jader Jed Francia2020-11-19T07:15:00ZTrasferire informazioni da IFrame a containerMarco Napolitanohttps://blog.d-vel.com/home/-/blogs/trasferire-informazioni-da-iframe-a-container2020-06-22T19:11:43Z2020-06-12T10:00:00Z<p>Oggi vi parlerò di IFrame e di Javascript per un problema che ho avuto utilizzando Liferay ma che in realtà può essere applicato anche in altri contesti.</p> <p>La necessità che avevo era quella di visualizzare una pagina di Liferay 7.2 all'interno di un IFrame di una pagina di Liferay 6.2; inoltre avevo bisogno che l'IFrame si ridimensionasse in altezza automaticamente in modo da evitare il classico problema della doppia barra di scorrimento laterale.</p> <p>I più attenti di voi potrebbero dire: "Liferay ha già una portlet IFrame che fa esattamente quello che ti serve". Vero, peccato però che il ridimensionamento dell'IFrame non funzioni perchè vengono messi in atto dei controlli di sicurezza per cui Javascript non è in grado di recuperare dall'IFrame le informazioni necessarie per determinarne l'altezza; e nemmeno l'IFrame è in grado di invocare funzioni Javascript sul container esterno, per lo stesso motivo.</p> <p>Inoltre Liferay non consente che le sue pagine siano incluse in un IFrame (la famigerata <em>Same Origin policy</em>), quindi di problemi da risolvere ce ne sono diversi; ma procediamo per passi.</p> <p>Il primo problema che risolviamo è fare in modo che le pagine di Liferay 7.2 possano essere incluse in un IFrame; per questo dobbiamo creare il file <code>system-ext.properties</code> e posizionarlo nella cartella <code><tomcat>/webapps/ROOT/WEB-INF/classes</code>. Il contenuto del file sarà l'elenco numerale di tutte le URL che devono essere incluse nell'IFrame, come ad esempio:</p> <pre> http.header.secure.x.frame.options.0=/pagina-1 http.header.secure.x.frame.options.1=/web/guest/pagina-1 http.header.secure.x.frame.options.2=/en/page-1 http.header.secure.x.frame.options.3=/en/web/guest/page-2 http.header.secure.x.frame.options.4=... http.header.secure.x.frame.options.5=... </pre> <p>A questo punto possiamo salvare il file, riavviare il server di Liferay 7.2 e finalmente riusciremo ad includere le specifiche pagine in un IFrame su Liferay 6.2.</p> <p>Bene, ma ora come facciamo a ridimensionare automaticamente l'IFrame in altezza? Per ottenere il risultato voluto dobbiamo riuscire a trasferire informazioni dall'IFrame al container. Per fare ciò dobbiamo scomodare Javascript ed in particolare la funzione <code>postMessage</code> che consente di inviare "messaggi" da una finestra ad un'altra del browser (anche non IFrame). Non entro nei dettagli della funzione ma se siete curiosi <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage" target="_blank">leggete qui</a>.</p> <p>A questo punto, nella JSP della portlet da includere nell'IFrame aggiungiamo questo frammento di codice:</p> <pre> <aui:script use="aui-base"> <span style="font-weight: inherit;">var content = A.one('#content');</span> var height = content.height(); parent.postMessage('{"height": ' + height + '}', '*'); </aui:script></pre> <p>Il codice non fa altro che recuperare l'elemento del DOM che rappresenta il contenuto della pagina e calcolarne l'altezza in pixel; dopodichè invia alla finestra <em>parent</em> (ossia il container dell'IFrame) un messaggio. Nel caso specifico il messaggio è una stringa che rappresenta un oggetto JSON con tutte le informazioni necessarie, nel nostro caso l'altezza in pixel; fate attenzione perchè la funzione <code>postMessage</code> consente di inviare anche oggetti ma, se non ricordo male, il buon vecchio IE supporta invece solamente stringhe.</p> <p>Ora possiamo spostarci su Liferay 6.2 dove dobbiamo andare ad intercettare il messaggio inviato dall'IFrame; il <em>dove</em> è ovviamente la JSP che visualizza l'IFrame e quindi avete 2 possibilità (nessuna delle quali verrà analizzata in questo post perché immagino siate già in grado di farlo da soli):</p> <ol> <li>fare un hook sulla portlet IFrame di Liferay 6.2</li> <li>farvi una vostra portlet custom per includere l'IFrame</li> </ol> <p>Quello che interessa a me adesso è solamente farvi vedere il codice per intercettare il messaggio:</p> <pre> <aui:script use="aui-base"> var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; var listener = window[eventMethod]; // Ascolta i messaggi che arrivano dall'altra finestra listener(messageEvent, function(e) { var key = e.message ? "message" : "data"; // Contenuto del messaggio var data = e[key]; try { // Sapendo che il messaggio è in formato JSON, lo convertiamo <span style="white-space: pre;"> </span>var json = A.JSON.parse(data); <span style="white-space: pre;"> // Recupero del nodo del DOM che rappresenta l'IFrame </span> var iframe = A.one('#<portlet:namespace />iframe'); // Se il nodo esiste, ne imposto l'altezza if (iframe) iframe.setStyle('height', json.height); } catch(e) { console.error(e); } }, false); </aui:script></pre> <p>Forte vero? Chiaramente vi ho fatto vedere solamente come modificare l'altezza ma nel messaggio potete inviare qualunque informazione e farne poi quello che volete.</p>Marco Napolitano2020-06-12T10:00:00ZModificare le etichette dell'elemento toggle-switchMarco Napolitanohttps://blog.d-vel.com/home/-/blogs/modificare-le-etichette-dell-elemento-toggle-switch2020-03-06T14:40:56Z2020-03-06T14:30:00Z<p>Eccoci di nuovo qui, con il primo post del 2020 anche se siamo già a marzo...</p> <p>Oggi vi voglio parlare di qualcosa di piuttosto semplice ma che ha richiesto il solito tempo di indagine nei sorgenti di Liferay per capire se e come era possibile fare quanto richiesto.</p> <p>A partire dalla versione 7, Liferay consente di graficare le checkbox anche in un modo alternativo molto più accattivamente; in pratica anzichè dichiarare il campo booleano nel seguente solito modo (ipotizzando che <code>male</code> sia un campo booleano della nostra entità):</p> <p><code><aui:input name="male" /></code></p> <p>possiamo definirlo anche così:</p> <p><code><aui:input checked="<%= user.isMale() %>" name="male" type="toggle-switch" /></code></p> <p>Questo farà sì che a video, al posto della solita noiosa e bruttina checkbox, sia visualizzato un componente grafico molto più gradevole e moderno.</p> <p>Il problema di questa soluzione è che il componente grafico visualizza automaticamente l'etichetta <code>SI</code> oppure <code>NO</code> a seconda del valore del campo booleano; apparentemente non è possibile modificare queste etichette perchè il DTD della taglib non lo consente.</p> <p>Tuttavia, spulciando tra i sorgenti del portale, ho scoperto che la taglib supporta i <em>dynamic attributes</em> ossia, in parole povere, consente di specificare attributi non definiti nel DTD; guarda caso, tra i vari attributi, ci sono anche quelli per personalizzare le suddette etichette: <code>labelOff</code> e <code>labelOn</code>. Ecco quindi come definire il campo del form:</p> <p><code><aui:input checked="<%= user.isMale() %>" labelOff="female" labelOn="male" name="male" type="toggle-switch" /></code></p> <p>Vi lascio come esercizio quello di scoprire a cosa servono gli altri attributi dinamici supportati dalla taglib:</p> <ul> <li><code>autoComplete</code></li> <li><code>buttonIconOff</code></li> <li><code>buttonIconOn</code></li> <li><code>dayLight</code></li> <li><code>displayStyle</code></li> <li><code>format</code></li> <li><code>iconOff</code></li> <li><code>iconOn</code></li> <li><code>nullable</code></li> <li><code>timeFormat</code></li> </ul> <p>Enjoy!</p>Marco Napolitano2020-03-06T14:30:00ZMa dov'è la 'page.jsp' della mia taglib standard di Liferay? :/Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/ma-dov-e-la-page-jsp-della-mia-taglib-standard-di-liferay-2019-11-16T15:21:37Z2019-11-16T14:42:00Z<p>Ciao a tutti e bentrovati sul nostro blog!</p> <p>Oggi pomeriggio, mentre m'annoiavo, ho deciso di risolvere uno dei puzzle più grandi che m'attanagliano da sempre su Liferay: capire dove finiscono (o dove hanno infilato.. :)) le JSP che sono richiamate dalle taglib di portale!</p> <p><em>[NdJ: lo so, non è una di quell cose da non doverci dormir di notte, però sapete che non ho una vita semplice, quindi abbiate pietà di me! ;D]</em></p> <p>Prima di svelarvi il segreto, però, vorrei farvi comprendere il caso d'uso, così che, se dovesse capitare anche a voi, possiate arrivare celermente ad una soluzione!</p> <h2>Taglib core di prodotto e JSP che ne implementano la view</h2> <p>Una volta mi sono trovato a dover mettere mano all'output di una taglib e, di conseguenza, mi sono fiondato nella classe java ce ne implementava la logica e sono andato a prendermi il nome della pagina JSP corrispondente per modificarla.</p> <p>Ora non ricordo più quale fosse quella volta la taglib, però, per semplicità, prendiamo come esempio la <span style="color: rgb(127, 130, 130); font-family: Monaco, Menlo, Consolas, "Courier New", monospace; font-size: 12px; white-space: pre; background-color: rgb(247, 247, 249);">com.liferay.taglib.ui.IconDeleteTag</span><span style="font-weight: inherit;">.</span></p> <p>Se aprite questa classe e andate a cercare la JSP, troverete questo statement:</p> <div><code>private static final String _PAGE = "/html/taglib/ui/icon_delete/page.jsp";</code></div> <p> </p> <p>Allora, come spesso faccio quando parto per la tangente ;), sono partito a cercare sul FS il file incriminato ma...</p> <p class="p1"><span class="s1">jedd@ne:~/liferay-ce-portal-src-7.1.1-ga2$ find . -type f -name page.jsp | grep -i icon_delete</span></p> <p class="p1"><span class="s1">jedd@ne:~/liferay-ce-portal-src-7.1.1-ga2$</span></p> <p> </p> <p>Niente! Della mia JSP non c'è traccia! :(</p> <p>Allora, sconfortato e sempre pronto a reinventar la ruota come se non ci fosse un domani, sarò sicuramente partito a creare la mia versione di taglib per sistemare il problema che avevo (non ricordo, ma conoscendo i tempi che ho a disposizione io quando faccio questo genere di cose, può essere che non mi sbagli poi di tantissimo.. :)).</p> <h2>E invece, come sempre, bastava leggere! :)</h2> <p>Se io avessi investito più di 7 secondi per analizzare il codice della taglib, <em>forse</em> (dico forse perché non è detto.. :D) mi sarei accorto che<span style="font-weight: inherit;"> all'inizio del metodo </span><code style="font-weight: inherit;">getPage()</code><span style="font-weight: inherit;"> c'è questa </span><span style="font-weight: inherit;">implementazione:</span></p> <div> <div><code>if (FileAvailabilityUtil.isAvailable(servletContext, _PAGE)) {</code></div> <div><code> return _PAGE;</code></div> <div><code>}</code></div> </div> <p> </p> <p>la quale mi avrebbe fornito un indizio molto interessante, ovvero che il concetto "<strong>IF IS AVAILABLE" </strong>può significare, per logica ;), che <strong>il file potrebbe anche non esserci</strong>.. Ah, ecco perché non lo trovavo.. :D</p> <h2>Allora da dove viene preso il codice che genera l'output della taglib?</h2> <p>Chiaramente il codice è presente nel resto dell'implementazione del metodo getPage(); ora però c'è da capire il <em>perché</em> e, soprattutto, come dovrei fare io se volessi <strong>modificarne l'output</strong>.</p> <h2>Perché l'output della taglib è dentro al metodo getPage()?</h2> <p>Chiaramente, al solito, i nostri amici hanno ottimizzato l'implementazione per far si che l'overhead di rendition delle singole taglib -che, come avrete notato anche voi in LR sono usate <em>parecchio</em>.. :D- sia super ridotto! A tal fine, quindi, hanno semplicemente <strong>eliminato</strong> le singole <code>page.jsp</code> e portato il codice che esse contenevano all'interno del metodo <code>getPage()</code>.</p> <p>Come faccio ad affermare che il motivo è questo? :)</p> <p>Beh, continuate a leggere.. :D</p> <h2>Ok ma.. Come faccio a modificare l'output di una taglib di LR?</h2> <p>Questa cosa l'ho scoperta, invece, navigando sul FS e cianfrusagliando tra i sorgenti del portale..</p> <p>Ho trovato dentro le taglib dei file chiamati <code>page.jsp.readme</code> che, stranamente :D, ho aperto per leggerli..</p> <p>Guardate che cosa ci ho trovato dentro:</p> <pre> <cite>The content of this JSP file was inlined to improve performance. To use your own content instead, remove the ".readme" extension and add your content to this file.</cite></pre> <p> </p> <p>Eggià: avevo proprio ragione!</p> <p>I file sono scomparsi perché hanno integrato la logica nel <code>getPage()</code> e se volessi modificarne l'output mi basterebbe seguire il suggerimento!</p> <p>Molto bene! A questo punto, però, ripenso al comando che ho dato prima.. E lo rilancio aggiustandolo:</p> <p> <style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Andale Mono'; color: #00ff00; background-color: #000000; background-color: rgba(0, 0, 0, 0.9)} span.s1 {font-variant-ligatures: no-common-ligatures} </style> </p> <p class="p1"><span class="s1">jedd@ne:~/liferay-ce-portal-src-7.1.1-ga2$ find . -type f -name page.jsp.readme | grep -i icon_delete</span></p> <p class="p1"><span class="s1">jedd@ne:~/liferay-ce-portal-src-7.1.1-ga2$ </span></p> <p> </p> <p>Ma come?? Non c'è il mio file!!</p> <p>Quindi?? Come faccio a modificare il mio output?</p> <p>Beh, questa volta il gioco è semplice: siccome il mio <code>getPage()</code> s'aspetta che la JSP possa esserci, dovrò solo fare in modo di fargliela trovare nel punto giusto del FS!</p> <p>E per farlo, mi basterà fare un <a href="https://portal.liferay.dev/docs/7-1/tutorials/-/knowledge_base/t/jsp-overrides-using-osgi-fragments" target="_blank">fragment host</a> per fargliela trovare! :)</p> <p>Anche per oggi è tutto; spero d'aver chiarito anche a tutti voi uno dei misteri più grandi di Liferay che da anni m'attanaglia la vita.. </p> <div class="portlet-msg-info">... E concludo dicendo che la <strong>seniority</strong>, se proprio vogliamo, <strong>sta anche nel saper leggere il codice</strong>! <span style="font-weight: inherit;">Cosa che, evidentemente, come dice anche l'Ing. Napolitano :D,<strong> io ancora non ho imparato a fare!</strong></span></div> <p>Passate un buon fine settimana e divertitevi (magari non con i sorgenti di LR, se potete! :D), voi che potete!</p> <p>Alla prossima!</p> <style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Andale Mono'; color: #00ff00; background-color: #000000; background-color: rgba(0, 0, 0, 0.9)} span.s1 {font-variant-ligatures: no-common-ligatures} </style>Jader Jed Francia2019-11-16T14:42:00ZEstendere Component in package internalPaolo Gambettihttps://blog.d-vel.com/home/-/blogs/estendere-component-in-package-internal2019-11-11T14:49:27Z2019-11-11T11:48:00Z<p>Nella vostra esperienza con Liferay vi potreste trovare ad aver bisogno di dover rimpiazzare o estendere @<span style="font-weight: inherit;">Component contenuti all'interno di package di Liferay internal. Quando i package sono "internal" non sono esportati pertanto anche dichiarando la dipendenza dal package nel file build.gradle del vostro modulo, questa, nella migliore delle ipotesi, non darà errori a tempo di compilazione ma produrrà un errore a runtime per dipendenza non risolta.</span></p> <p>Vediamo come si può ovviare a questo problema basandoci sull'esempio di una implementazione che ho dovuto realizzare.</p> <p>L'implementazione consisteva essenzialmente nel realizzare una ricerca customizzata a livello di condizioni di ricerca e visualizzazione dei risultati prodotti appoggiandosi il più possibile alla ricerca nativa di Liferay. La versione di Liferay utilizzata è la 7.1.3.</p> <p>La mia ricerca doveva essere effettuata solo per contenuti di tipo "KBArticle". In certe condizioni che non specificherò perchè non inerenti alla soluzione della problematica, quello che succedeva era che nell'operazione di costruzione delle query per ElasticSearch, venissero create condizioni basate su entità diverse dalla "KBArticle", ad esempio Wiki, JournalArticle etc, e perciò non di nostro interesse ai fini della produzione dei risultati custom corretti.</p> <p>Avevo perciò la necessità di intervenire a livello del component:</p> <pre> <em>@Component(immediate = true, service = PreFilterContributorHelper.class)</em> <em>public class PreFilterContributorHelperImpl</em> <em>implements PreFilterContributorHelper {</em></pre> <div> </div> <div>per limitare la creazione delle query di prefiltraggio alla sola entità "KBArticle".</div> <div> </div> <div>La classe PerFilterContributorHelperImpl è però contenuta all'interno del package <em>com.liferay.portal.search.internal.indexer </em>nel jar <em>"com.liferay.portal.search-5.0.26.jar"</em>che come dice il nome è un package internal quindi non esportato.</div> <div> </div> <div>Esiste uno stratagemma per esportare un package internal. Vediamo come fare:</div> <div>ho creato un bundle Liferay Module Project Fragment che ho chiamato portal-search-filter.</div> <div> </div> <div>Nel file bnd.bnd del modulo portal-search-filter ho esportato il package di mio interesse "com.liferay.portal.search.internal.indexer" in questo modo:</div> <div> </div> <div> <pre> <em>Bundle-Name: portal-search-filter Bundle-SymbolicName: portal.search.filter Bundle-Version: 1.0.0 Fragment-Host: com.liferay.portal.search;bundle-version="5.0.26"</em> <em>Export-Package: com.liferay.portal.search.internal.indexer</em></pre> </div> <div>Attenzione che nel frammento non ho selezionato di fare l' "override" di nessun file .jsp in quanto l'unico e solo scopo del frammento è quello di esportare un package di Liferay di tipo internal. </div> <div> </div> <div>A questo punto ho potuto creare un mio @Component custom che estende la classe PreFilterContributorHelperImpl con un service.ranking più elevato del service di default in modo da mandarlo in esecuzione. Così:</div> <div> </div> <div> <pre> <em>@Component(immediate = true, service = PreFilterContributorHelper.class, property = {</em> <em>"service.ranking:Integer=1000"</em> <em>})</em> <em>public class PreFilterContributorHelperCustom</em> <em>extends PreFilterContributorHelperImpl</em> <em>implements PreFilterContributorHelper </em></pre> <div> </div> <div>Nella classe <em>PreFilterContributorHelperCustom </em>ho potuto fare i seguenti import:</div> <div> </div> <pre> <em>import com.liferay.portal.search.internal.indexer.PreFilterContributorHelper; import com.liferay.portal.search.internal.indexer.PreFilterContributorHelperImpl;</em></pre> <div> </div> <div>package che sono esportarti dal Fragment portal-search-filter che ho creato allo scopo in precedenza.</div> <div> </div> <div>A questo punto ho raggiunto il mio scopo, posso tranquillamente fare l'override dei metodi di PreFilterContributorHelperImpl e variare il comportamento della mia classe perchè crei query di filtraggio su ElasticSearch sulla sola entity KBArticle.</div> <div> </div> <div> </div> </div>Paolo Gambetti2019-11-11T11:48:00ZThreadLocal.. Centralized! ;)Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/threadlocal-centralized-2019-11-04T11:15:45Z2019-11-04T06:03:00Z<p>Buongiorno a tutti e ben tornati alle cronache di Jader e dei suoi <em>animali mitologici</em>! ;)</p> <p>Questa mattina vi racconterò una nuova puntata della saga, oramai divenuta celeberrima :), sugli <em>animali mitologici</em> e i loro casi d'uso un po' fuori dall'ordinario!</p> <p>In realtà, ad essere del tutto onesti, qui il caso d'uso non m'è stato imposto da loro <em>direttamente</em>, però diciamo che la colpa rimane loro perché.. Sono loro che hanno scelto lo strumento del male per il quale ho dovuto scomodare dei nuovi amici!</p> <p>Ma entriamo subito nel vivo, così che anche voi possiate beneficiare di questa funzionalità di Liferay!</p> <p>Questa volta però, prima del caso d'uso, devo darvi un po' di contesto!</p> <h2>RTIR: il sistema di ticket da interfacciare!</h2> <p>In queste settimane mi è toccata la parte più noiosa del -chiamiamolo così..- lavoro: ho dovuto fare un refactor (nemmeno fossi l'Ingegner Napolitano quando apre il mio codice! :D) di un client REST per interfacciare RTIR: un sistema di ticket molto gradito al Cliente.</p> <p>RTIR, per chi non lo sapesse è una nuovissima e fiammante web interface scritta nientemeno che in <strong>perl</strong> (si, nel 2019 ancora il perl..) di cui hanno fatto ben 2 release di API REST. [NdJ: "nuova e fiammante", onestamente, è una battuta...]</p> <p>Oddio.. In tutta onestà chiamarle "API REST" è forse un po' troppo: come potrete vedere dall'abbondante e copiosa documentazione (<a href="https://rt-wiki.bestpractical.com/wiki/REST" target="_blank">versione 1.0</a> e <a href="https://github.com/bestpractical/rt-extension-rest2" target="_blank">versione 2.0</a>), tutto sono tranne che API disegnate come si deve! </p> <p>Diciamo che quando hanno fatto quella roba lì (insieme inclusivo anche della <strong>pagina singola di documentazione</strong> per ogni release dell'API..), forse non avevano bene in mente che qualcuno avrebbe dovuto un giorno invocarle davvero.. </p> <p>Tuttavia, siccome m'è toccato, come si dice spesso ;), mi sono messo di buona lena e ho interfacciato queste API facendo quello che andava fatto (non lato codice, lato pratico: mi sono tappato il naso e mi sono messo a fare senza rompere -troppo- le scatole! :D).</p> <h2>Cookie, autenticazioni, bearer token, trust manager.. Nient'altro?? :D</h2> <p>Senza perdermi oltre nel criticare un sistema che sicuramente sarà fichissimo (tanto da essere stato scelto dal Cliente dei miei <em>animali mitologici</em> come sistema di ticket interno), mi sono lanciato nel disegnare tutti gli aspetti della nostra interfaccia, considerando chiaramente anche gli aspetti di configurazione.</p> <p>Nel farlo, ho scoperto che mi sarebbero serviti diversi flag (con relativi parametri):</p> <ul> <li><span style="font-weight: inherit;"><strong>autenticazione basata su Basic Auth</strong>: vuoi non proteggerlo dal web?</span></li> <li><span style="font-weight: inherit;"><strong>autenticazione basata su bearer token</strong>: vuoi non metterlo, in una API REST?</span></li> <li><span style="font-weight: inherit;"><strong>autenticazione basata su user / pass</strong>: eggià, nel 2019 questo è uno dei metodi <em>consigliati</em>... </span></li> <li><strong>utilizzo di un trust manager custom</strong>: vuoi non installare un certificato <em>self signed</em>, quando uno vero ormai te lo danno gratis?</li> </ul> <p>Chiaramente, nel mio modello, più di uno di questi meccanismi doveva essere implementato pena il non funzionamento dell'intero cinema!</p> <p>E mentre facevo tutto questo, sul quale tralascio ovviamente la parte di codice perché non funzionale a questo post, mi rendevo conto che avrei avuto <em>un leggerissimo problema di performance</em>: per come stavo strutturando il client (legato per indirettezza a come sono disegnate le API...), avrei avuto metodi stateless che, a singola invocazione, avrebbero dovuto invocare <strong>due</strong> URL per singola chiamata.</p> <p>Questo meccanismo "a doppia chiamata" si rendeva necessario proprio per i flag di cui sopra: la prima chiamata sarebbe servita per ottenere l'autenticazione (cookie, token, quel che l'è), la seconda per invocare effettivamente l'API.</p> <p>Peccato che il grafo da navigare di questo coso sia oscenamente grosso: inutile dire che, a fronte del caricamento del dettaglio di un ticket, mi trovo a dover invocare 6 / 7 URL che, moltiplicate per la fase di autenticazione, diventano 12 / 14 URL..</p> <div class="portlet-msg-info">Faccio presente che questo <strong>perverso meccanismo a multi chiamata per avere il dettaglio di un ticket</strong> è normato dal design della API di cui sopra, quindi non sono io che non ho idea di come fare un client REST, sono loro che non hanno idea di come disegnare una API.. :/</div> <p>Avendo costruito però tutto il client REST (ed essendo anche soddisfatto del mio lavoro, nonostante le critiche che l'Ingegner Napolitano mi muoverebbe se vedesse il mio codice.. :D), mi scocciava ridisegnarlo da capo per evitare questo problema..</p> <p>E allora mi sono detto: <em>"visto il codice sul quale ho fatto refactor, nessun s'accorgerà della melma che ho disegnato e forse la passerò liscia.."</em> ;D</p> <p>Ma poi, più lo utilizzavo per farci i test e più mi rendevo conto che no, dai, era davvero osceno: sia in termini di performance sia in termini di design.. Per quanto i miei <em>animali mitologici</em> mi torturino, non mi sembrava il caso di lasciare loro del codice fatto così <em>ammmelma</em>!</p> <h2>Però la pigrizia la faceva da padrona..</h2> <p>Siccome lo statement qui sopra è sempre vero ;), avendo io disegnato un client che fa esattamente tutto quello che mi serviva e non volendolo ridisegnare completamente ;), mi sono interrogato su come rendere più performante il mio codice!</p> <p>Chiaramente, evitare il throttling delle URL sarebbe la prima cosa da fare; però come avrei potuto farlo se tutte le URL da invocare fanno riferimento a pojo differenti e devono essere riutilizzabili anche in metodi differenti? Inoltre, come potevo rendere la API indipendente dall'implementazione fisica del client REST senza necessariamente dover creare un accoppiamento con questo nelle firme?</p> <p>Ed è stato proprio qui, mentre pensavo a tutte queste cose, che m'è venuto in mente un post di Daniele Catellani sul <a href="https://blog.d-vel.com/home/-/blogs/il-fascino-dei-threadlocal" target="_blank">fascino dei thread local</a>, fatto davvero tantissimi anni fa!</p> <p>Però poi mi sono anche ricordato dello sbatti mostruoso che bisogna fare per usarli (non in termini di codice, ma in termini di <em>gestione</em>) [NdJ: se non avete idea di cosa io sto dicendo ;), <a href="https://liferay.dev/blogs/-/blogs/master-your-threadlocals" target="_blank">questo articolo di LR fa al caso vostro</a>..).</p> <h2>E allora, come al solito ;), mi sono messo a scavare.. :D</h2> <p>A questo punto già avrete intuito cosa mi sono messo a fare! Come spesso accade lavorando con Liferay ;), in più di un milione di righe di codice, <em>sicuramente</em> esiste qualche cosa che fa già quello che mi serve.. :)</p> <p>Quindi, anziché partire in quarta a inventare il meccanismo definitivo custom per la gestione dei thread local, avendone visti implementati in LR a bizzeffe :D, mi sono messo a cercare e ho trovato i miei nuovi amici: i <code>CentralizedThreadLocal</code>! :)</p> <h2>CentralizedThreadLocal: chi sono e cosa fanno!</h2> <p>Questa classe è stata progettata per risolvere in maniera molto semplice ed elegante una serie di problemi che esistono nella gestione dei thread local. Ad esempio, permette di ripulire il, chiamiamolo così..., <em>singleton</em> del thread local specifico per ogni request, evitandovi lo sbatti di doverlo fare voi.</p> <p>Chiaramente, per farlo usa sempre i "soliti" meccanismi: c'è un simpatico filtro che si occupa di ripulire il thread local dopo ogni invocazione, così da non doverci caricare dell'onere di farlo.</p> <p>A questo punto, però, dobbiamo vedere insieme un po' di codice, per capire nel dettaglio come implementarne uno e come fare in modo che tutti i nostri problemi, magicamente, si dissolvano! :D</p> <div class="portlet-msg-info">Piccola premessa: la classe CentralizedThreadLocal si trova all'interno di due package di prodotto; la prima, il kernel, dove questa è deprecata e la seconda, dentro a petra.lang, che è quella che dobbiamo utilizzare!</div> <p><code>package it.dvel.rtir.client.util;</code></p> <p><code>import aQute.bnd.annotation.ProviderType;<br /> import com.liferay.petra.lang.CentralizedThreadLocal;</code></p> <p><code>import javax.ws.rs.core.NewCookie;<br /> import java.util.List;</code></p> <p><code>@ProviderType<br /> public class RtirCookieThreadLocal {</code></p> <p><code> public static List<NewCookie> getCookies() {<br /> return _newCookies.get();<br /> }</code></p> <p><code> public static void setCookies(List<NewCookie> cookies) {<br /> _newCookies.set(cookies);<br /> }</code></p> <p><code> private static final ThreadLocal<List<NewCookie>> _newCookies =<br /> new CentralizedThreadLocal<>(<br /> RtirCookieThreadLocal.class + "._newCookies",<br /> () -> null);</code></p> <p><br /> <code>}</code></p> <p>Come potete notare, la classe è molto stupida: di fatto è un wrapper sull'oggetto <code>ThreadLocal</code> che però viene valorizzato con una istanza di <code>CentralizedThreadLocal</code>.</p> <p>A questa istanza vengono passati due dettagli importanti: come si chiama il <code>ThreadLocal</code>, all'interno della mappa che il padre di tutti questi oggetti si crea (quindi questo nome dev'essere univoco perché interpretato come un id..) e una lamda che fornisce un'implementazione della <code>@FunctionalInterface Supplier</code>, ovvero del wrapper per la costruzione del valore di default del nostro <code>ThreadLocal</code>.</p> <p>Di default, usando il costruttore che abbiamo utilizzato noi, viene anche impostato internamente al <code>ThreadLocal</code> il flag <code>shortLived</code> a <code>true</code>, il che significa che il nostro oggetto sarà ripulito "al termine di ogni esecuzione della richiesta".</p> <p>Ed ecco ottenuto il nostro <code>ThreadLocal</code> nuovo fiammante e assolutamente in grado di risolvere -elegantemente, aggiungerei ;)- anche il problema della condivisione all'interno del nostro thread delle informazioni necessarie a non dover riautenticare il Client ad ogni richiesta!</p> <p>Ma le magie del <code>CentralizedThreadLocal</code> non finiscono qui!</p> <p>Se andate a vedere nel codice sorgente quali altre opportunità vi offre :), scoprirete una serie di feature davvero interessanti..</p> <p>.. Però qui io mi fermo!</p> <p><strong>Adesso è il vostro turno</strong>: aprite il codice e divertitevi a scoprire quali e quante cose può fare per voi il nostro nuovo amico e, come al solito ;), se volete potete commentare in questo post le vostre scoperte, così da renderle utili a tutti!!!</p> <p>Anche per oggi è tutto, buon divertimento con i <code>CentralizedThreadLocal</code>, Liferay e.. I vostri <em>animali mitologici</em>!! :)</p> <p>A presto, ciao, J.</p>Jader Jed Francia2019-11-04T06:03:00ZSincronizzare permessi tra modelli differenti!Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/sincronizzare-permessi-tra-modelli-differenti-2019-10-15T05:55:18Z2019-10-14T08:45:00Z<p>Buongiorno a tutti e ben ritrovati con "Le cronache di Jader", appunti -quasi- sensati per sopravvivere al difficile mondo dell'IT su Liferay!</p> <p>Oggi vi parlerò di un problema che <em>i miei animali mitologici</em> mi hanno posto la scorsa settimana: mantenere sincronizzati i permessi tra due modelli differenti!</p> <p>Come al solito, partirò raccontandovi il <em>perché</em> di questo requisito con un caso d'uso che spero sia sensato, così che possiate anche voi trarne spunto e capire velocemente se fa al caso vostro (non il caso d'uso, la soluzione! :)).</p> <h1>Mantenere sincronizzati i permessi di due modelli differenti: caso d'uso</h1> <p>Il caso d'uso è semplice: ho due oggetti (nel mio caso un oggetto custom che chiameremo, con enorme fantasia ;), <code>ModelloCustom</code> che al suo interno ha una referenza ad un <code>DlFileEntry</code>.</p> <p>Il caso d'uso vuole che, a prescindere da quale delle due entità io parta a modificare i permessi, i permessi dell'altra entità siano mantenuti sincroni.</p> <p>Quindi, per spiegarmi più semplicemente, se io modifico i permessi su <code>ModelloCustom</code> aggiungendo, che ne so, il permesso di <code>VIEW</code> e il permesso di <code>UPDATE</code> a uno specifico ruolo, coerentemente con questo aggiornamento mi troverò modificati gli stessi due permessi sullo stesso ruolo dell'entità <code>DlFileEntry</code>.</p> <h1>Cose da NON fare per gestire questo caso d'uso!</h1> <p><span style="font-weight: inherit;">Prima di dirvi come fare a gestire questo caso :), vi dico quello che </span><em style="font-weight: inherit;">non</em><span style="font-weight: inherit;"> dovete fare per gestirlo! :)</span></p> <p>Intendo che si, anche io ci ho messo un po' per risolverlo e, nel mio "tentare", ho provato diverse strade per riuscirci.</p> <h2>Abusare dei value object listener: cattiva idea.. :/</h2> <p>La prima strada che ho percorso era quella dei <em>value ojbect listener</em>: ho creato dei listener (che dovrebbero servire a gestire l'integrità referenziale ma dei quali io ho <em>abusato</em> per i miei scopi.. ;)), uno per <code>ModelloCustom</code> e uno per <code>DlFileEntry</code> intercettando la modifica all'evento relativo sull'entità <code>ResourcePermission</code>.</p> <p>Sembrava funzionare ;), però poi mi sono accorto che gli update finivano per "rompere" i permessi; nel senso: le update venivano propagate correttamente ma, non so il perché :), quando il pannellino dei permessi si ricaricava questo mostrava permessi <em>diversi</em> da quelli che avevo impostato.</p> <p>Sicuramente <em>i miei animali mitologici</em> si sarebbero lamentati di questo comportamento un po' bizzarro ;), quindi ho scartato questa implementazione e ho proseguito nel mio scouting..</p> <h2>Service Wrapper: panacea di tutti i miei mali?</h2> <p>Ovviamente la risposta è.. NO!</p> <p>Posto che non ho capito perché ma la 7.1.2 non mi caricava il <code>ServiceWrapper</code> che avevo creato (o meglio: caricare lo caricava, però non lo invocava e questa cosa mi aveva un po' tediato..) ho pensato che comunque non sarebbe stata una gran implementazione: il service della <code>ResourcePermission</code> espone, #chevelodicoafare :), mille milioni di metodi e scoprire quali fossero quelli giusti mi sembrava già di per se troppo sbatti.. :)</p> <p>Forte del fatto poi che non riuscivo a far invocare il mio <code>ServiceWrapper</code> mi sono arreso in -credo- 8 minuti scartando anche questa strada!</p> <h2>E allora.. Custom sia! :)</h2> <p>Confrontandomi con l'<em>animale mitologico</em> che nel frattempo stava umoralmente cambiando il suo mood nei confronti della cosa, sentendolo sempre più disperato e triste (e con l'avvicinarsi del terribile momento del Collaudo col Cliente -fase della vita di un (quasi) dev che mette sempre una certa adrenalina, ammettiamolo! :D) mi sono detto "beh, sai che c'è? Che se il pannellino maledetto standard non fa quello che voglio io, allora me ne faccio uno custom dove piloto quello che mi pare sui permessi e amici come prima!".</p> <p>L'idea, parlandone anche con il mio <em>animale mitologico</em>, sembrava addirittura sensata! Ci siamo lasciati alla fine della telefonata super convinti che questa fosse LA strada!</p> <p>... E invece no!</p> <p>Questa cosa del custom a tutti i costi non è mai una buona idea, soprattutto quando lavori con un mastodonte come Liferay!</p> <p>E quindi? Che ho fatto?</p> <p>Beh, premetto che.. A posteriori "la cosa del custom a tutti i costi non è mai una buona idea", ma sul momento.. Ero partito per implementarla! :)</p> <p>E siccome sono uno pigro mi son detto "non è che devo reinventare la ruota: guardiamo come fa Liferay a gestire la costruzione del pannellino e come legge poi i parametri dalla matrice Azioni / Ruoli che disegna e replico tutto nel mio pannellino custom!".</p> <p>Mamma mia quanto sono pigro e furbo.. :D</p> <p>Per fortuna so ancora leggere il codice (anche se faccio queste cose tipo alle 4 della mattina, ma vabbé..) e, facendo scouting del codice della <code>com.liferay.portlet.configuration.web.internal.portlet.PortletConfigurationPortlet</code> e più precisamente il suo simpa metodo <code>updateRolePermissions</code> ho conosciuto dei nuovi amici.. :)</p> <h1>PermissionPropagator: ecco la risposta che cercavo! ;)</h1> <div class="portlet-msg-info">Spoiler ON: faccio presente che l'obiettivo funzionale per il quale i PermissionPropagator sono stati inventati NON è quello al quale ambisco io. In realtà questi cosi sono stati progettati per Propagare sulle entità figlie i permessi (ad esempio se cambio il permesso di visibilità su un thread del forum e vorrei che tutte le risposte al suo interno fossero automaticamente allineate senza doverle modificare a mano..).</div> <p>Però è anche vero che, alla fin fine, il mio obiettivo è <em>simile</em>: anche io vorrei che si propagassero i permessi tra il mio <code>ModelloCustom</code> e la mia <code>DlFileEntry</code>!</p> <p>Quindi ho proceduto, senza indugio, a testare questa soluzione!</p> <p>Vediamo un po' di codice, per capire se questa roba ha senso anche per voi! :)</p> <h2>Annotiamo la classe come Component</h2> <p><code>@Component(<br /> immediate = true,<br /> property = {<br /> "javax.portlet.name=" + PortletKeys.MODELLO_CUSTOM,<br /> "javax.portlet.name=" + PortletKeys.DOCUMENT_LIBRARY_ADMIN<br /> },<br /> service = PermissionPropagator.class<br /> )</code></p> <p>Siccome io volevo <strong>bidirezionalmente</strong> aggiornare i permessi, quindi sia che si partisse dai permessi di <code>ModelloCustom</code> sia che si partisse dai permessi del <code>DlFileEntry</code>, ho mappato il mio <code>PermissionPropagator</code> su entrambe le portlet di gestione!</p> <h2>Come sempre esiste una Base.. :)</h2> <p><code>public class MyPermissionPropagator extends BasePermissionPropagator {</code></p> <p><code>...</code></p> <p><code>}</code></p> <p><span style="font-weight: inherit;">Anche in questo caso ho esteso la classe base che Liferay mette sempre a disposizione...</span></p> <h2>Implementiamo il metodo richiesto dall'interfaccia!</h2> <p><code>@Override<br /> public void propagateRolePermissions(ActionRequest actionRequest,<br /> String className, String primKey, long[] roleIds) throws PortalException { <br /> if (ModelloCustom.class.getName().equals(className)) {<br /> propagateRolePermissionsToFileEntry(actionRequest, className, primKey, roleIds);<br /> } else if (DLFileEntry.class.getName().equals(className)) {<br /> propagateRolePermissionsToModelloCustom(actionRequest, className, primKey, roleIds);<br /> }<br /> }</code></p> <p>Ho poi implementato il metodo che l'interfaccia <code>PermissionPropagator</code> richiede per funzionare!</p> <h2>Ho sfruttato le facility che la Base mi mette a disposizione! ;)</h2> <p><code>private void propagateRolePermissionsToModelloCustom(ActionRequest actionRequest,<br /> String className, String primKey, long[] roleIds) throws PortalException {<br /> long fileEntryId = GetterUtil.getLong(primKey);<br /> ModelloCustom modelloCustom = modelloCustomLocalService.fetchByFileEntryId(fileEntryId);<br /> if (Validator.isNotNull(modelloCustom)) {<br /> for (long roleId : roleIds) {<br /> propagateRolePermissions(actionRequest, roleId, DLFileEntry.class.getName(),<br /> fileEntryId, ModelloCustom.class.getName(), modelloCustom.getPrimaryKey());<br /> }<br /> }<br /> }</code></p> <p><code> public void propagateRolePermissionsToFileEntry(ActionRequest actionRequest,<br /> String className, String primKey, long[] roleIds) throws PortalException {<br /> ModelloCustom modelloCustom = modelloCustomLocalService.getModelloCustom(GetterUtil.getLong(primKey));<br /> if (modelloCustom.getFileEntry() > 0) {<br /> for (long roleId : roleIds) {<br /> propagateRolePermissions(actionRequest, roleId, ModelloCustom.class.getName(),<br /> modelloCustom.getPrimaryKey(), DLFileEntry.class.getName(), modelloCustom.getFileEntry());<br /> }<br /> }<br /> }</code></p> <h2>È qui che accade la magia! :)</h2> <p>La chiamata alla <code>propagateRolePermissions</code> è chiaramente quella che sfrutto per far accadere la magia e, nemmeno a dirlo, è quella messa a disposizione dalla mia Base!</p> <p>Cosa fa di magico questo metodo?</p> <p>In pratica fa una cosa molto.. Pratica! :)</p> <p>Si preoccupa di capire quali sono i permessi in comune tra le due entità (mappati nella tabella <code>ResourceAction</code>) e poi li sincronizza! Tuto gratis, tutto trasparente e, soprattutto, tutto.. Che funziona! :)</p> <p>Beh, anche per oggi abbiamo imparato qualche cosa di nuovo (cfr. "smettila di pensare che il custom sia l'unica via per perseguire l'obiettivo: su più di 1.000.000 di righe di codice ci sarà qualche cosa che fa già al caso tuo, no?:)") e siamo pronti a propagare permessi come se non ci fosse un domani! ;)</p> <p>Alla prossima e divertitevi con Liferay 7.x, OSGi e... I vostri <em>animali mitologici</em>!! :)</p> <p>A presto, ciao, J.</p> <p>P.S. Stavo quasi per dimenticarmi!</p> <p>Aggiungete questa riga nel vostro <code>portal-ext.properties</code> altrimenti non funzionerà mai l'implementazione che avete appena fatto!</p> <p><code>##<br /> ## Permissions<br /> ##<br /> #<br /> # Set the following to true to enable propagation of permissions between<br /> # models.<br /> #<br /> # For example, when setting the permissions on a specific Wiki node, if you<br /> # assign a role a permission (e.g. DELETE), then the assignment of that<br /> # permission is also propagated to all Wiki pages that belong to that Wiki<br /> # node.<br /> #<br /> # The actual logic of how permissions are propagated among models is<br /> # specified per portlet. See liferay-portlet.xml's use of the element<br /> # "permission-propagator".<br /> #<br /> # Env: LIFERAY_PERMISSIONS_PERIOD_PROPAGATION_PERIOD_ENABLED<br /> #<br /> permissions.propagation.enabled=true</code></p> <p> </p>Jader Jed Francia2019-10-14T08:45:00ZUtilizzare HTMLComponent con JasperSoftStudio 6.3.1.final e Liferay 7.1.1Katia Pazzihttps://blog.d-vel.com/home/-/blogs/utilizzare-htmlcomponent-con-jasperreport-su-liferay-7-1-12019-06-27T15:03:53Z2019-06-27T12:23:00Z<p> </p> <p>Quando si desidera visualizzare un contenuto HTML all'interno di un report PDF, TIBCO JasperSoft Studio</p> <p>offre due possibilità:</p> <p><strong>1</strong>- impostare <strong>Markup/HTML</strong> in fase di design del template .jrxml;</p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"><strong>2</strong>- utilizzare <strong>HTMLComponent</strong>.</span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">La <strong>soluzione</strong> <strong>1</strong>, non richiede di importare librerie aggiuntive e quindi è molto veloce, </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">ma </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"> </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">l'insieme dei tag </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">HTML che vengono convertiti correttamente in PDF è molto ridotto, </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">ed in particolare tag di uso </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">molto </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">comune </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">come <strong>, <style>, <em> e <img> vengono ignorati.</span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">La <strong>soluzione 2</strong> è un poco più articolata, ma offre l'indubbio vantaggio di convertire correttamente un </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">numero </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">maggiore </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">di tag HTML.</span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"><strong>HTMLComponent</strong> è una libreria prodotta da JasperSoft, che è stata ed è ancora ampiamente utilizzata, </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">come </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">testimoniano i numerosi thread di discussione reperibili online.</span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Al momento, però, non viene distribuita dai repository Maven per il download e non è disponibile sulla </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">palette </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">degli </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">strumenti di TIBCO JasperSoft Studio 6.3.1.final.</span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Tuttavia <strong>JasperSoft Studio</strong> deve continuare a gestirla per ragioni di retrocompatibilità, e quindi, di fatto </span></p> <p><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">quando </span><span style="display: inline !important; float: none; background-color: transparent; color: rgb(0, 0, 0); font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">nel template .jrxml trova questo codice:</span></p> <p><font color="#3333ff"> </font><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><band</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">height</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"742"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><componentElement></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><reportElement</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">x</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"0"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">y</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"0"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">width</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"555"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">height</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"742"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">/></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><hc:html</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">xmlns:hc</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"http://jasperreports.sourceforge.net/htmlcomponent"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span></p> <p><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">xsi:schemaLocation</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"http://jasperreports.sourceforge.net/htmlcomponent </span></p> <p><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">http://jasperreports.sourceforge.net/xsd/htmlcomponent.xsd"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span></p> <p><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">scaleType</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"RetainShape"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">horizontalAlign</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"Left"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">verticalAlign</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"Top"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><hc:htmlContentExpression></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><![CDATA[$P{myHtmlContent}]]></span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></hc:htmlContentExpression></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></hc:html></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></componentElement></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></band></span></p> <p>JasperSoft Studio è in grado di mostrare tutte le properties dell 'HTMLComponent in formato visuale e</p> <p>consente di modificarle in modo assistito.</p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">In aggiunta, le versioni più recenti di </span><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">JasperSoft Studio, come la 6.3.1 in questione, consentono di utilizzare </span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">HTMLComponent all'interno del tool di palette '<strong>Generic Element</strong>' indicando i seguenti valori.</span></p> <p><span style="font-size:16px;"><i><font color="#242729">Generic Type Name</font></i><span style="text-align: left; color: rgb(36, 39, 41); text-transform: none; line-height: 19.5px; text-indent: 0px; letter-spacing: normal; font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; word-spacing: 0px; display: inline !important; white-space: normal; orphans: 2; font-size-adjust: none; font-stretch: 100%; float: none; -webkit-text-stroke-width: 0px; background-color: transparent;">: </span><font color="#242729"><strong style="background-color: transparent; border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(36, 39, 41); font-family: Arial,&quot;Helvetica Neue&quot;,Helvetica,sans-serif; font-size: 15px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 700; letter-spacing: normal; line-height: 19.5px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"><em style="border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; font-family: Arial,&quot;Helvetica Neue&quot;,Helvetica,sans-serif; font-size: 15px; font-size-adjust: none; font-stretch: 100%; font-style: italic; font-variant: normal; font-weight: 700; line-height: 19.5px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"><code style="background-color: rgb(239, 240, 241); border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: italic; font-variant: normal; font-weight: 700; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 1px; padding-left: 5px; padding-right: 5px; padding-top: 1px; vertical-align: baseline; white-space: pre-wrap;">htmlelement</code></em></strong></font></span><br style="text-align: left; text-transform: none; text-indent: 0px; letter-spacing: normal; font-size: 15px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; word-spacing: 0px; white-space: normal; box-sizing: border-box; orphans: 2; -webkit-text-stroke-width: 0px; background-color: transparent;" /> <span style="font-size:16px;"><em style="background-color: transparent; border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(36, 39, 41); font-family: Arial,&quot;Helvetica Neue&quot;,Helvetica,sans-serif; font-size: 15px; font-size-adjust: none; font-stretch: 100%; font-style: italic; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 19.5px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Generic Type Namespace</em><span style="text-align: left; color: rgb(36, 39, 41); text-transform: none; line-height: 19.5px; text-indent: 0px; letter-spacing: normal; font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; word-spacing: 0px; display: inline !important; white-space: normal; orphans: 2; font-size-adjust: none; font-stretch: 100%; float: none; -webkit-text-stroke-width: 0px; background-color: transparent;">: </span><strong style="background-color: transparent; border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(36, 39, 41); font-family: Arial,&quot;Helvetica Neue&quot;,Helvetica,sans-serif; font-size: 15px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 700; letter-spacing: normal; line-height: 19.5px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"><em style="border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; font-family: Arial,&quot;Helvetica Neue&quot;,Helvetica,sans-serif; font-size: 15px; font-size-adjust: none; font-stretch: 100%; font-style: italic; font-variant: normal; font-weight: 700; line-height: 19.5px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"><code style="background-color: rgb(239, 240, 241); border-bottom-color: rgb(36, 39, 41); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(36, 39, 41); border-left-style: none; border-left-width: 0px; border-right-color: rgb(36, 39, 41); border-right-style: none; border-right-width: 0px; border-top-color: rgb(36, 39, 41); border-top-style: none; border-top-width: 0px; box-sizing: border-box; font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: italic; font-variant: normal; font-weight: 700; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 1px; padding-left: 5px; padding-right: 5px; padding-top: 1px; vertical-align: baseline; white-space: pre-wrap;"><font color="#242729">http://jasperreports.sourceforge.net/jasperreports/html</font></code></em></strong></span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">In questo modo tutte le proprietà e i parametri possono essere visualizzate all'interno del pannello di </span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">properties </span><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">del 'Generic Element', </span><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">anche se la loro modifica è meno assistita che nel caso precedente, </span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">trattandosi di un tool generico.</span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Il risultato del nostro operato sarà, ad esempio, il seguente frammento sul template .jrxml.</span></p> <p><font color="#3333ff"> </font><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><band</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">height</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"742"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElement></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><reportElement</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">x</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"0"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">y</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"0"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">width</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"555"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">height</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"742"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">/></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElementType</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">namespace</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"http://jasperreports.sourceforge.net/jasperreports/html"</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span></p> <p><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">name</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"htmlelement"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">/></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElementParameter</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">name</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"htmlContent"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><valueExpression></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><![CDATA[$P{myHtmlContent}]]></span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></valueExpression></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></genericElementParameter></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElementParameter</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">name</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"scaleType"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><valueExpression></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><![CDATA["RetainShape"]]></span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></valueExpression></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></genericElementParameter></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElementParameter</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">name</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"verticalAlign"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><valueExpression></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><![CDATA["Top"]]></span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></valueExpression></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></genericElementParameter></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><genericElementParameter</span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="atn" style="background-color: transparent; border-bottom-color: rgb(230, 67, 32); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(230, 67, 32); border-left-style: none; border-left-width: 0px; border-right-color: rgb(230, 67, 32); border-right-style: none; border-right-width: 0px; border-top-color: rgb(230, 67, 32); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(230, 67, 32); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">name</span><span class="pun" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">=</span><span class="atv" style="background-color: transparent; border-bottom-color: rgb(15, 116, 189); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(15, 116, 189); border-left-style: none; border-left-width: 0px; border-right-color: rgb(15, 116, 189); border-right-style: none; border-right-width: 0px; border-top-color: rgb(15, 116, 189); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(15, 116, 189); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">"horizontalAlign"</span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><valueExpression></span><span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"><![CDATA["Left"]]></span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></valueExpression></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></genericElementParameter></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></genericElement></span><br /> <span class="pln" style="background-color: transparent; border-bottom-color: rgb(48, 51, 54); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(48, 51, 54); border-left-style: none; border-left-width: 0px; border-right-color: rgb(48, 51, 54); border-right-style: none; border-right-width: 0px; border-top-color: rgb(48, 51, 54); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(48, 51, 54); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"> </span><span class="tag" style="background-color: transparent; border-bottom-color: rgb(125, 39, 39); border-bottom-style: none; border-bottom-width: 0px; border-image-outset: 0; border-image-repeat: stretch; border-image-slice: 100%; border-image-source: none; border-image-width: 1; border-left-color: rgb(125, 39, 39); border-left-style: none; border-left-width: 0px; border-right-color: rgb(125, 39, 39); border-right-style: none; border-right-width: 0px; border-top-color: rgb(125, 39, 39); border-top-style: none; border-top-width: 0px; box-sizing: border-box; color: rgb(125, 39, 39); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; font-size: 13px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: 16.9px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; orphans: 2; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; vertical-align: baseline; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;"></band></span><br /> </p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Comunque la si utilizzi, la libreria HTMLComponent, necessita al momento di essere configurata su Liferay </span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">mediante i seguenti passaggi.</span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">Queste istruzioni si riferiscono al progetto in cui è già installato e funzionante JasperReport, cioè in cui sono </span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">state dichiarate le dipendenze dalle librerie di JasperReport.</span></p> <p><span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">1- Copiare nella cartella </span><strong>src/main/resources/lib</strong> tutti <span style="background-attachment: scroll; background-clip: border-box; background-color: transparent; background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; background-size: auto; color: rgb(0, 0, 0); display: inline; float: none; font-family: &quot;Roboto Slab&quot;,Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">i jar contenuti in nella cartella </span></p> <p><span style="color:#0000FF;"> Jaspersoft Studio-6.3.1.final\configuration\org.eclipse.osgi\38\0\.cp\lib</span></p> <p>2- Aggiungere al <strong>build.gradle</strong> le dipendenze</p> <p> <span style="color:#0000FF;"> dependencies {</span></p> <p><span style="color:#0000FF;"> compileInclude name: 'htmlcomponent'<br /> compileInclude name: 'core-renderer'<br /> compileInclude name: 'jtidy-r938'</span></p> <p><span style="color:#0000FF;"> //oppure, se si preferisce, <span style="text-align: left; text-transform: none; text-indent: 0px; letter-spacing: normal; font-family: "Roboto Slab",Times New Roman,serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; word-spacing: 0px; display: inline !important; white-space: normal; orphans: 2; float: none; text-shadow: none; -webkit-text-stroke-width: 0px; background-color: transparent;">'jtidy' può essere scaricato dai repository Maven</span>: </span></p> <p><span style="color:#0000FF;"> //compileInclude group: 'net.sf.jtidy', name: 'jtidy', version: 'r938'</span></p> <p><span style="color:#0000FF;"> }</span></p> <p>3- Indicare sul <b>build.gradle</b> la locazione in cui si trovano queste librerie</p> <div> <span style="color:#0000FF;">repositories {</span></div> <div><span style="color:#0000FF;"> flatDir {<br /> dirs 'src/main/resources/lib'<br /> }</span></div> <div><span style="color:#0000FF;"> }</span></div> <div> </div> <div> </div> <p>4- Aggiungere queste esclusioni al file <b>bnd.bnd</b></p> <p><font color="#3333ff"> !org.xhtmlrenderer.test,\<br /> !org.xhtmlrenderer.test.*,\</font></p> <p>A questo punto si è pronti per produrre i PDF partendo da un contenuto HTML.</p> <p>Va ricordato che tutto il contenuto HTML viene trasformato da questo componente in un' unica immagine,</p> <p>che pertanto potrebbe essere visualizzata diversamente rispetto ad un browser HTML e a seconda del</p> <p>formato di esportazione utilizzato.</p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p>Katia Pazzi2019-06-27T12:23:00ZValorizzare i placeholder di un PDF con iText7 e Liferay 7.1Marco Napolitanohttps://blog.d-vel.com/home/-/blogs/valorizzare-i-placeholder-di-un-pdf-con-itext7-e-liferay-7-12019-06-11T07:42:59Z2019-06-11T07:30:00Z<p>Correva l'anno 2009 quando una giovane Serena scriveva un interessante post sulla modalità con cui valorizzare i placeholder all'interno di un PDF; per chi se lo fosse perso vi invito a leggere il <a href="http://blog.d-vel.com/home/-/blogs/come-valorizzare-i-placeholder-nei-template-pdf">post originale</a>.</p> <p>Sono passati ormai un pò di anni, i sistemi sono cambiati ma le esigenze sono rimaste le stesse ed infatti oggi mi sono ritrovato a dover fare la medesima cosa: valorizzare i placeholder all'interno di un PDF.</p> <p>Il post originale è stato di grande ispirazione ma purtroppo alcune librerie sono cambiate ed ho dovuto fare qualche modifica al codice; modifica che voglio condividere con voi... e con me stesso quando tra qualche mese dovrò rifarlo nuovamente!</p> <p>Innanzitutto io sto lavorando con Liferay 7.1.3 e di conseguenza la prima cosa da fare è configurare correttamente tutte le dipendenze. Nel mio caso utilizzerò solamente alcuni pacchetti forniti dalla libreria <a href="https://itextpdf.com/en/products/itext-7">iText7</a>; pertanto aggiungiamo le seguenti righe all'interno del file <code>build.gradle</code>:</p> <pre> compileInclude group: "com.itextpdf", name: "kernel", version: "7.1.6" <span style="font-weight: inherit;">compileInclude group: "com.itextpdf", name: "forms", version: "7.1.6"</span></pre> <p>Queste sono le uniche librerie che ci servono perchè l'unica cosa che dobbiamo fare è valorizzare i placeholder all'interno di un PDF che già esiste; la versione 7.1.6 è la più recente al momento in cui sto scrivendo questo post.</p> <p>In realtà queste librerie si portano dietro altre dipendenze, che a noi non servono, pertanto andiamo anche ad inserire le seguenti righe all'interno del file <code>bnd.bnd</code>:</p> <pre> Import-Package: \ !org.bouncycastle.*,\ !org.slf4j.impl.*,\ *</pre> <p>A questo punto facciamo un bel "gradle refresh" e deployamo tutto per verificare che le dipendenze siano tutte soddisfatte.</p> <p>Veniamo ora al codice da usare per valorizzare i placeholder e salvare il file di output; siccome stiamo parlando di file e di Liferay, diamo per scontato che il codice seguente sia inserito all'interno del metodo <code>serveResource</code> della nostra portlet in modo da consentirci di salvarlo.</p> <pre> import com.itextpdf.forms.PdfAcroForm; import com.itextpdf.forms.fields.PdfFormField; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; // Recupero dalla Document Library del PDF contenente i placeholder FileEntry fileEntry = ... // Creazione reader del PDF PdfReader reader = new PdfReader(fileEntry.getContentStream()); // File di output File file = FileUtil.createTempFile(); PdfWriter writer = new PdfWriter(file); // Creazione del documento PdfDocument pdfDocument = new PdfDocument(reader, writer); // Form contenente i campi del PDF da riempire PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDocument, true); form.setGenerateAppearance(true); // Recupero e valorizzazione dei placeholder Map<String, PdfFormField> fields = form.getFormFields(); fields.get("nome_campo_1").setValue("..."); fields.get("nome_campo_1").setValue("..."); fields.get("nome_campo_1").setValue("..."); // Valorizzare dei placeholder rimanenti // Rende i placeholder del PDF non modificabili form.flattenFields(); pdfDocument.close(); writer.close(); reader.close(); String fileName = fileEntry.getFileName(); // Download del file PortletResponseUtil.sendFile( resourceRequest, resourceResponse, fileName, FileUtil.getBytes(file), MimeTypesUtil.getContentType(file));</pre> <div>Chiaramente potete anche decidere si salvare il file di output nella Document Libray anzichè farne il download...</div>Marco Napolitano2019-06-11T07:30:00ZAggiungere link custom alla portlet di login di Liferay 7.1Marco Napolitanohttps://blog.d-vel.com/home/-/blogs/aggiungere-link-custom-alla-portlet-di-login-di-liferay-7-12019-05-08T07:00:28Z2019-05-08T07:00:00Z<p>Fortunamente non ho <em>animali mitologici</em> da gestire come Jader, tuttavia però... ho Jader :)</p> <p>Oggi voglio parlare un pò di OSGi illustrandovi una soluzione molto elegante per risolvere un problema che potrebbe capitarvi.</p> <p>Il requisito iniziale, a parole, è molto semplice: rimuovere il link <em>Password dimenticata</em> dalla portlet di login nativa di Liferay 7.1 ed aggiungere un link custom che punti ad una pagina in cui è presente una portlet custom.</p> <p>In sostanza il risultato finale deve essere il seguente (sotto al pulsante <em>Accedi</em>):</p> <p><img alt="" src="https://blog.d-vel.com/documents/185441/186313/liferay_71_login.png/c7f18b1d-4073-4ba2-9b15-7910c2af9522?t=1556869998397" /></p> <p>Soddisfare il primo requisito (rimuovere il link <em>Password dimenticata</em>) è semplice perchè si tratta di una configurazione da fare sull'istanza di Liferay.</p> <p>Invece per aggiungere un link custom la cosa è un pochino diversa; se fossimo sulla versione 6.2 avrei già iniziato a parlare di hook della JSP di portale e sinceramente il mio approccio iniziale era di fare qualcosa di analogo anche sulla 7.1. Così ho iniziato a cercare nei sorgenti di portale il punto in cui venivano gestiti i link ed ho scoperto che... non venivamo gestiti! Non esplicitamente almeno. Cioè?!</p> <p>Il file in cui vengono gestiti i link a fondo pagina nella portlet di login è il seguente:</p> <pre> [SRC]\modules\apps\login\login-web\src\main\resources\META-INF\resources\navigation.jspf</pre> <p>Ma al suo interno non ci sono link da modificare ma solamente taglib del tipo:</p> <pre> <liferay-util:dynamic-include key="com.liferay.login.web#/navigation.jsp#post" /></pre> <p>In pratica, per farla breve, tutti i link presenti vengono inclusi dinamicamente a runtime (<code>dynamic-include</code> per l'appunto); pertanto non bisogna modificare la portlet di login ma semplicemente <em>realizzare</em> un opportuno componente OSGi che venga caricato a runtime dalla taglib e si occupi di <em>renderizzare</em> il link custom.</p> <p>Non mi soffermo oltre e vi mostro qualche riga di codice!</p> <pre> @Component(immediate = true, property = { "login.web.navigation.position=post", "service.ranking:Integer=100" }, service = PageInclude.class) public class MyNavigationPostPageInclude implements PageInclude { @Reference private LayoutLocalService _layoutLocalService; @Override public void include(PageContext pageContext) throws JspException { try { // La request serve sempre... HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); // Sito di riferimento long groupId = PortalUtil.getScopeGroupId(request); // Recupero la pagina a cui deve puntare il link custom Layout layout = _layoutLocalService .fetchLayoutByFriendlyURL(groupId, false, "/my-friendly-url"); if (layout != null) { /* * Se il testo del link non è una traduzione di portale, allora * è necessario recuperarla esplicitamente dal vostro bundle */ Locale locale = PortalUtil.getLocale(request); ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(locale, this.getClass()); String message = LanguageUtil.get( resourceBundle, "link-text-label"); // Creazione della render URL da associare al link custom LiferayPortletURL myURL = PortletURLFactoryUtil.create( request, PortletKeys.MY_CUSTOM_PORTLET, layout, PortletRequest.RENDER_PHASE); // Istanzio e configuro via codice la taglib aui:icon IconTag iconTag = new IconTag(); iconTag.setCopyCurrentRenderParameters(false); iconTag.setIconCssClass("icon-undo"); iconTag.setLocalizeMessage(false); iconTag.setMessage(message); iconTag.setUrl(myURL.toString()); iconTag.doTag(pageContext); } } catch (PortalException e) { throw new JspException(e); } } }</pre> <div>Non so voi, ma io la trovo una soluzione estremamente elegante che evita di mettere mano ai sorgenti di portale.</div>Marco Napolitano2019-05-08T07:00:00ZLiferay 7.1: taglib multiple in un unico bundle!Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/liferay-7-1-taglib-multiple-in-un-unico-bundle-2019-05-06T21:16:28Z2019-05-06T21:16:00Z<p>Buongiorno a tutti e ben trovati sul nostro blog! ;)</p> <p>Il post di oggi sarà super rapido e semplicissimo: di fatto, come spiego anche nell'intro, mi sono trovato a dover <strong>centralizzare taglib che vivevano in <code>bundle</code> differenti all'interno di un unico <code>bundle</code> centralizzato</strong>.</p> <p>Fin qui la cosa potrebbe sembrare semplice: fai un po' di copia / incolla / trascina (per i più sofisticati potrei definirlo <em>refactor</em>.. ;)) ma poi.. Fai deploy e scopri che non funziona più nulla!</p> <p>Come mai? Perché? Cosa <strong>NON</strong> ho copiato? Cosa ho sbagliato?</p> <p>Prima di farmi attanagliare da tutti questi dubbi, come spesso faccio :), mi sono buttato a capofitto nei sorgenti di Liferay 7.1 e ho scoperto che la cosa, da sistemare, era davvero semplice!</p> <h1>bnd.bnd: come sempre, tutto parte da qui!</h1> <p>Anche questa volta il titolo del capitolo non è molto evocativo ;), però è un periodo durante il quale la mia creatività è bassa.. Non me ne vogliate!</p> <p>Però il titolo è molto vero; infatti la prima cosa da fare è modificare il file <code>bnd.bnd</code> (che poi altro non è che il responsabile di quello che finisce nel manifest del nostro <code>bundle OSGi</code>) per dirgli:</p> <ul> <li>che, ovviamente, fornisce come servizio delle <code>taglib</code>;</li> <li>che abbiamo più di una <code>taglib</code> all'interno del <code>bundle</code>.</li> </ul> <p>Per fare questa magia, quindi, ho modificato così il mio bnd.bnd:</p> <pre> Bundle-Name: dvel-util-taglib Bundle-SymbolicName: it.dvel.playground.util.taglib Bundle-Version: 1.0.0 Export-Package: \ it.dvel.playground.util.taglib.search.tag,\ it.dvel.playground.util.taglib.rubrica.tag Provide-Capability:\ osgi.extender;\ osgi.extender="jsp.taglib";\ uri="http://www.d-vel.com/tld/dvel";\ version:Version="${Bundle-Version}",\ osgi.extender;\ osgi.extender="jsp.taglib";\ uri="http://www.d-vel.com/tld/rubrica";\ version:Version="${Bundle-Version}" Web-ContextPath: /dvel-taglib </pre> <p>La parte interessante è chiaramente quella che riguarda la property <code>Provide-Capability</code>; a questa, infatti, faccio corrispondere una <em>lista</em> di osgi.extender dove, per ognuno di essi, specifico la uri della singola taglib.</p> <p>Fatto questo il problema è risolto! Ora non ci resta che usare il nostro modulo e quindi le nostre taglib!</p> <p>Come fare? Semplice: nelle singole JSP dove ci servono, ci basterà importare la taglib (sempre nella <code>init.jsp</code>, mi raccomando!):</p> <pre> <%@ taglib prefix="rubrica" uri="http://www.d-vel.com/tld/rubrica" %></pre> <p>e poi.. Utilizzarla:</p> <pre> <rubrica:listUtil var="listUtil" /></pre> <p>Come detto, questo post è super veloce e molto semplice.. Se sapevate come fare questa cosa!!</p> <p>Spero di avervi dato un buon suggerimento; se vi occorrono maggiori dettagli o siete semplicemente curiosi di conoscere qualche altro dettaglio su questo tema, come sempre, usate l'area dei commenti e saremo ben lieti di aiutarvi!!</p> <p>A presto e buon divertimento con Liferay 7.1, le taglib (centralizzate tutte in un unico modulo) e i casi funzionali degli <em>animali mitologici</em> più strani che ci siano! :)</p> <p>Alla prossima!!</p>Jader Jed Francia2019-05-06T21:16:00ZReference OSGi: filtri e query per injection non banali!!Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/reference-osgi-filtri-e-query-per-injection-non-banali-2019-05-02T09:30:28Z2019-05-02T09:30:00Z<p>Buongiorno a tutti!</p> <p>Eccomi di nuovo da voi per un post su Liferay 7.1 e le magie di OSGi!</p> <p>Anche oggi vorrei raccontarvi quello che i miei tanti <em>animali mitologici</em> mi hanno chiesto qualche tempo fa e come sono riuscito a risolvere il problema!</p> <p>Per farlo, però, come sempre vi devo raccontare il caso d'uso, così che l'implementazione possa essere quantomeno <em>sensata</em>.. :)</p> <h1>Caso d'uso: codice centralizzato ma.. Specializzato per ogni modulo!</h1> <p>Lo so: la cosa più assurda dei miei post sono i requisiti che i miei <em>animali mitologici</em> riescono a darmi! :)</p> <p>Però credetemi: quando me li danno me li motivano e, almeno sulla carta, sembra che abbiano pure un senso! </p> <p>Nello specifico, questa volta, quello che mi hanno chiesto è stato proprio.. Creare del codice che dovrà essere centralizzato (per ovvie ragione) ma che.. Debba essere specializzato per ogni modulo! Il motivo è semplice:</p> <ul> <li><strong>centralizzato</strong>: perché alla fine la logica è uguale in tutti i punti dove viene utilizzato;</li> <li><strong>specializzato</strong>: perché vogliono predisporsi al cambiamento; nel caso la logica standard non vada più bene in un modulo, vogliono semplicemente cambiarne l'implementazione in quel modulo e tutto deve continuare a funzionare.</li> </ul> <p>Come al solito, nemmeno a farlo apposta, <em>tutti a me capitano</em>.. :D</p> <p>Ora che abbiamo definito che i miei <em>animali mitologici</em> sono <em>particolari</em> :), andiamo a vedere come ho risolto questi due vincoli, sfruttando una delle caratteristiche di OSGi che usate spesso ma alla quale, magari, non avete mai <em>prestato particolare attenzione</em>..</p> <h1>Coding time!</h1> <p>Partiamo subito facendo vedere il codice che ho centralizzato; qui, ovviamente, non mi soffermo sul <em>cosa</em>, perché è abbastanza indifferente. Mi concentro di più sul <em>come</em>, che è la parte un po' più interessante dell'implementazione.</p> <pre> package it.dvel.playground.search; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.filter.BooleanFilter; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.search.spi.model.query.contributor.ModelPreFilterContributor; import com.liferay.portal.search.spi.model.registrar.ModelSearchSettings; import it.dvel.playground.util.search.SearchContextConstants; import org.osgi.service.component.annotations.Component; import java.util.Date; @Component( immediate = true, property = "model.pre.filter.contributor.id=DateRangeFilter", service = ModelPreFilterContributor.class ) public class DateRangePreFilterContributor extends BaseDateRangePreFilterContributor { @Override public void contribute(BooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings, SearchContext searchContext) { Date startDate = (Date) searchContext.getAttribute(SearchContextConstants.SEARCH_BY_FROM_DATE); Date endDate = (Date) searchContext.getAttribute(SearchContextConstants.SEARCH_BY_TO_DATE); if (Validator.isNotNull(startDate) && Validator.isNotNull(endDate)) { endDate = setTime(endDate, 23, 59); if (_log.isDebugEnabled()) { _log.debug("Start Date: " + startDate); _log.debug("End Date: " + endDate); } String field = (String) searchContext.getAttribute("it.dvel.playground.search.common.field.date.range"); addFilter(field, booleanFilter, startDate, endDate); } } private static final Log _log = LogFactoryUtil.getLog(DateRangePreFilterContributor.class); }</pre> <p>Vi manca metà del mondo degli oggetti della modellazione, però avete l'unico che conta: l'oggetto che definisco <strong>centralizzato</strong>! Questo oggetto, che nel dettaglio serve a scatenare un filtro sulla ricerca attraverso l'indice nel caso siano presenti due date (lasciate stare il perché, dai.. :)), ha una particolarità: definisce un suo ID attraverso una property.</p> <p>Questa è la riga incriminata:</p> <pre> property = "model.pre.filter.contributor.id=DateRangeFilter",</pre> <p>Questa è la caratteristica interessante, e presto vedremo il perché!</p> <h1>Injection di oggetti con.. Filtro!</h1> <p>Il secondo requisito è quello della <strong>specializzazione</strong>: l'idea di base dei miei <em>animali mitologici</em> era quella di essere <strong>pronti al cambiamento</strong>! Siccome però, per definizione, gli <em>animali mitologici</em> sono come vogliamo vederli noi :), i miei erano pigri (almeno quanto me :)) e quindi <strong>volevano a tutti i costi</strong> che ogni modulo specializzato avesse:</p> <ul> <li>una sua classe specifica per questo tipo di filtro;</li> <li>che la classe specifica facesse allegramente uso dell'implementazione centralizzata.</li> </ul> <p>So che per i più questo, nel mondo del software ad oggetti, si chiama banalmente <code>proxy</code>, però qui la cosa si fa un po' più complicata, perché parliamo di Servizi rilasciati attraverso <code>bundle</code> differenti nel container OSGi.</p> <p>A questo punto.. Come fare a risolvere il requisito?</p> <p>Bhe, come dice il titolo di questo capitolo (e spoilerato poi anche dal <em>dettaglio</em> del capitolo precedente.. ;)), ci basterà solamente <strong>filtrare gli oggetti che ci vengono iniettati</strong> e il gioco sarà fatto!</p> <p>Ecco quindi come ho implementato l'oggetto all'interno del modulo specializzato, usando chiaramente il concetto di <code>proxy</code> sull'oggetto centralizzato!</p> <pre> package it.dvel.playground.avvisi.search; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.filter.BooleanFilter; import com.liferay.portal.search.spi.model.query.contributor.ModelPreFilterContributor; import com.liferay.portal.search.spi.model.registrar.ModelSearchSettings; import it.dvel.playground.util.search.FieldConstants; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @Component( immediate = true, property = "indexer.class.name=it.dvel.playground.avvisi.model.Avviso", service = ModelPreFilterContributor.class ) public class AvvisiDateRangePreFilterContributor implements ModelPreFilterContributor { @Override public void contribute(BooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings, SearchContext searchContext) { searchContext.setAttribute("it.dvel.plaground.search.common.field.date.range", FieldConstants.AVVISO_DATA_REDAZIONE); dateRangeFilterPreFilterContributor.contribute(booleanFilter, modelSearchSettings, searchContext); } @Reference(target = "(model.pre.filter.contributor.id=DateRangeFilter)") protected ModelPreFilterContributor dateRangeFilterPreFilterContributor; private static final Log _log = LogFactoryUtil.getLog(AvvisiDateRangePreFilterContributor.class); }</pre> <p>La magia è tutta qui:</p> <pre> @Reference(target = "(model.pre.filter.contributor.id=DateRangeFilter)") protected ModelPreFilterContributor dateRangeFilterPreFilterContributor; </pre> <p>Nella <code>Reference</code>, infatti, io specifico <em>indirettamente</em> che vorrei un oggetto di tipo <code>ModelPreFilterContributor</code> però sono selettivo: <strong>non mi accontento di averne uno a caso</strong> ma voglio proprio quello che mi pare a me, quello che ha ID che vale <code>DateRangeFilter</code>.</p> <p>Ed è il container OSGi a fare la magia, popolandomi il field interno con l'oggetto specifico!</p> <p>Ovviamente questo tipo di implementazione sfrutta la caratteristica <strong>dell'iniezione selettiva</strong> degli oggetti fatta dal container OSGi, però, chiaramente, è basata su un assunto banale, ovvero che esista un solo <code>ModelPreFilterContributor</code> che ha quell'ID.</p> <p>Questo è un assunto molto forte, però è funzionale all'esempio che volevo mostrarvi!</p> <p>Adesso la palla è nel vostro campetto: se qualcuno ha idea di come rendere più robusta l'implementazione può scriverlo nei commenti, così da confrontarci e crescere tutti insieme, ovviamente migliorandoci! :)</p> <p>Fino ad allora, come al solito, divertitevi e fatemi sapere se vi torna tutto quello che ho scritto!</p> <p>Se avete domande o dubbi, potete scriverli nei commenti e sarò / saremo ben lieti di aiutarvi!!</p> <p>Buon divertimento a tutti con OSGi, Liferay 7.1 e i filtri sulle iniezioni degli oggetti! :)</p> <h1>Materiale utile alla discussione</h1> <ul> <li>Javadoc dell'implementazione di <a href="https://osgi.org/javadoc/r4v43/core/org/osgi/framework/Filter.html" target="_blank">Filter</a> nel core di OSGi</li> <li>La <a href="https://www.ietf.org/rfc/rfc1960.txt" target="_blank">RFC1960</a> che rappresenta come i filtri di LDAP vadano costruiti (sintassi e logica): comodo perché è da qui che sono derivati i filtri sugli oggetti nel container OSGi</li> </ul>Jader Jed Francia2019-05-02T09:30:00ZLiferay 7.1 e ServiceTracker: nuovi amici utili e molto comodi! ;)Jader Jed Franciahttps://blog.d-vel.com/home/-/blogs/liferay-7-1-e-service-tracker-nuovi-amici-utili-e-molto-comodi-2019-04-30T20:26:04Z2019-04-30T15:41:00Z<p>Ciao a tutti!</p> <p>Ammetto che mi fa un po' strano scrivere di nuovo sul blog <img alt="smiley" height="20" src="http://blog.d-vel.com/html/js/editor/ckeditor/plugins/smiley/images/regular_smile.gif" title="smiley" width="20" />, soprattutto perché <em>qualcuno </em><strong>che ha sicuramente più tempo di me per farlo</strong> in effetti ha preso un bel distacco sul numero di post effettuati! </p> <p>Visto che però, finalmente, anche io ho trovato argomenti interessanti da postare :D, ho pensato di rifarmi vivo per condividere con voi quello che ho scoperto giocando con Liferay 7.1!</p> <p>Intanto una premessa: come al solito la vita di Jader è costellata di <em>animali mitologici</em>, spesso impegnati a far fare a Jader cose strane.. Come sempre vi chiedo indulgenza nel non deridermi sul perché queste creature mi chiedano certe cose ;), ma di concentrarvi sul caso funzionale e di commentare, se lo ritenete opportuno, l'implementazione tecnica!</p> <p>Venendo quindi all'oggetto del post, vi vado ad introdurre i miei nuovi amici, legati al mondo OSGi, che mi si è aperto con l'introduzione di Liferay 7: i Service Tracker.</p> <h1>Service Tracker: cosa sono e a cosa servono?</h1> <p>Allora, la prima cosa che vorrei spiegarvi è cosa sono e a cosa servono i service tracker nell'architettura OSGi, così da introdurveli e farvi comprendere come possono tornarci comodi per le nostre implementazioni su Liferay!</p> <p>Stranamente :), per comprenderli bene vi consiglierei di partire dalla <a href="https://dev.liferay.com/en/develop/tutorials/-/knowledge_base/7-1/service-trackers" target="_blank">documentazione ufficiale di Liferay</a> che, per una volta :), non è così incompleta, buggata e <code>{fair mode="on"}</code>"non così utile"<code>{/fair}</code> come siamo abituati a ricordarcela! :)</p> <p>Per chi fosse davvero pigro (o avesse poca dimestichezza con il mondo OSGi), il concetto lo si può riassumere davvero mooooolto velocemente così: i <code>ServiceTracker</code> sono gli oggetti che ci permettono di accedere al registry OSGi, interrogandolo a <strong>runtime</strong> sui servizi disponibili.</p> <p>Significa che questi oggetti sono il modo con cui possiamo:</p> <ul> <li>interrogare il container OSGi dicendogli "dammi tutti i servizi di un certo tipo";</li> <li>fare qualche cosa a runtime su questi servizi (cosa poi lo vediamo dopo.. ;))</li> </ul> <p>E qui già è spiegata parte della magia: i <code>ServiceTracker</code> ci permettono, a <strong>runtime</strong>, di sapere se / quali servizi sono presenti all'interno del motore.</p> <p>Ora voi ci chiederete <em>perché</em> una persona normale dovrebbe voler fare questo tipo di domanda al container, quando è noto dalla notte dei tempi che è possibile farseli iniettare senza troppo sforzo; beh, qui entriamo subito nel secondo pezzo dell'articolo!</p> <h1>Caso d'uso: wizard di raccolta dati di un'anagrafica</h1> <p>Ok, confesso che questo titolo non rende giustizia al caso specifico; detta così scomodare addirittura un <code>ServiceTracker</code> potrebbe sembrare inutile perché, lo abbiamo fatto tutti, un wizard potrebbe banalmente essere una sequenza di JSP che si richiamano tra di loro (in catena visto che è un wizard... :)) e che, una volta arrivato in fondo termina senza troppo sforzo.</p> <p>Per capire bene il mio caso d'uso, però, vi devo aggiungere dei dettagli funzionali che renderanno un po' più <em>pepata</em> la situazione e giustificheranno quindi l'utilizzo dei <code>ServiceTracker</code> all'interno del modello che andremo a realizzare!</p> <p>Il primo requisito che i miei <em>animali mitologici</em> mi hanno chiesto è, nemmeno a dirlo, il <strong>disaccoppiamento</strong>! Il wizard che dovremo creare dovrà quindi permettere loro di caricare <code>bundle</code> nel container e <strong>aggiungere così pagine al wizard</strong>. E qui, l'implementazione monolitica non è molto furba, considerando il fatto che loro hanno specificato chiaramente che vogliono aggiungere <code>bundle</code>, e non "modificare e rilasciare" sempre lo stesso con il wizard...</p> <p>Anzi: nella loro testa il wizard è solo un visualizzatore: quello che visualizza dovrebbe essere proprio discriminato dai vari <code>bundle</code>, che successivamente, questi caricheranno.</p> <p>Il secondo requisito che mi hanno dato è stato <strong>l'ordinamento</strong>: per definizione un wizard è composto di una serie arbitraria di pagine <em>in sequenza</em>. Quindi loro vorrebbero che il wizard fosse in grado, da solo, di discriminare l'ordine di visualizzazione (e di conseguenza anche quello di compilazione da parte dell'utente finale) delle singole pagine.</p> <p>Infine, l'ultimo requisito è relativo al <strong>comportamento</strong>: per farla facile, non tutte le pagine devono essere compilate da tutti gli utenti, quindi il wizard deve essere in grado di mostrare o meno la singola pagina a seconda di regole arbitrarie che saranno codificate nei singoli <code>bundle</code> rilasciati.</p> <h1>Architettura applicativa</h1> <p>Siccome non ho mai niente da fare perché, come molti di voi purtroppo sanno :), io per lavoro <em>non lavoro</em> :), avevo sentito parlare dei <code>ServiceTracker</code> e quindi, anche un po' per curiosità :), ho proposto di implementare il wizard in questo modo:</p> <ol> <li>un bundle relativo al wizard vero e proprio (quindi la <code>Portlet</code> da mettere in pagina);</li> <li>un bundle relativo all'interfaccia del servizio (che chiameremo <code>WizardController</code>);</li> <li>una serie di bundle che, implementando l'interfaccia <code>WizardController</code>, ci permetteranno di caricare a <em>runtime</em> le pagine del wizard.</li> </ol> <p>Ovviamente, dietro a tutto questo, nella mia testa c'era che il <code>ServiceTracker</code> mi avrebbe aiutato a realizzare la magia. E tra un po' vedremo come.. ;)</p> <p>Comunque sia, grazie a questa modularizzazione, ho pensato, abbiamo risolto il requisito numero uno, quello relativo al disaccoppiamento.</p> <p>Infatti, se tutto andrà come voluto ;), avremo effettivamente la possibilità di caricare <code>bundle</code> a raglio nel container OSGi e popoleremo in maniera <em>disaccoppiata</em> il nostro wizard. Questo perché, grazie al <code>ServiceTracker</code>, potremo modificare a runtime la nostra chain ogni volta che un <code>bundle</code> sarà aggiunto / rimosso dal container!</p> <p>Però, a meno di non cablare da qualche parte nella <code>Portlet</code> la logica di sorting delle pagine, non abbiamo ancora risolto il secondo requisito, quello relativo all'ordinabilità delle pagine.</p> <p>Per farlo in maniera furba (almeno: questo è quello che ho pensato io, ma sono aperto anche a soluzioni differenti), ho modellato il concetto di "catena di pagine" attraverso un oggetto che ho chiamato convenzionalmente <code>WizardChain</code>. Come potete ben intuire :), questo oggetto ha la responsabilità di rappresentare la catena delle pagine del wizard, così che io possa inserirvi all'interno la logica di sorting e quindi, all'occorrenza, sostituirla fornendone un'implementazione differente!</p> <p>Ancora una volta il requisito del disaccoppiamento è rispettato!</p> <p>Rimane da gestire il terzo requisito, quello del comportamento. Qui però la cosa è semplice: come avrete sicuramente già intuito, modellando con dei Servizi i singoli anelli della catena, il comportamento è dettato:</p> <ul> <li>in parte da come la <code>Portlet</code> interpreta il Servizio;</li> <li>in parte da come il Servizio è implementato.</li> </ul> <h1>E adesso.. Codice!! :)</h1> <p>Bene! Finalmente possiamo dare libero sfogo al codice, illustrandovi un po' come fare a gestire tutto quello che ho descritto qui sopra su Liferay 7.1.</p> <p>Partiamo dalla parte facile: la definizione del Servizio.</p> <p>Qui l'ho giocata facile, un Servizio, in termini assoluti, non è che l'implementazione di una interfaccia! E allora ecco la nostra interfaccia:</p> <pre> package it.dvel.playground.wizard.chain; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.service.ServiceContext; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import java.io.IOException; public interface WizardController { /** * Restituisce il path della JSP che visualizza il singolo passo del wizard. * @return La JSP da visualizzare all'interno del wizard. */ public String getJspPath(); /** * Vale <code>true</code> se il passo &egrave; gi&agrave; stato completato; <code>false</code> altrimenti. * @param user l'utente che deve eseguire il passo del wizard. * @param ctx Il <code>ServiceContext</code> relativo alla richiesta. * @return <code>true</code> se il passo &egrave; gi&agrave; stato completato; <code>false</code> altrimenti. * @throws PortalException In caso di errori nell'elaborazione. */ public boolean isCompleted(User user, ServiceContext ctx) throws PortalException; /** * Determina se il passo &egrave; applicabile all'utente corrente o meno. * @param user l'utente che deve eseguire il passo del wizard. * @return ritorna <code>true</code> se l'utente pu&ograve; eseguire il passo, <code>false</code> altrimenti. */ public boolean isValid(User user); /** * Processa la logica di business per il salvataggio dei dati del singolo step. * @param request La <code>ActionRequest</code> di richiesta. * @param response La <code>ActionResponse</code> di risposta. * @throws PortletException In caso di errori nell'elaborazione (controllati o meno). * @throws IOException In caso di errori nell'elaborazione (controllati o meno). */ public void processLogic(ActionRequest request, ActionResponse response) throws PortletException, IOException; /** * Determina se una eccezione &egrave; o meno gestita all'interno della pagina. * @param cause Ritorna <code>true</code> se l'eccezione &egrave; controllata a livello di pagina, <code>false</code> altrimenti. * @return <code>true</code> se l'eccezione &egrave; controllata a livello di pagina, <code>false</code> altrimenti. */ // public boolean isSessionError(Throwable cause); }</pre> <p>Senza girarci troppo intorno ;), ho definito un'interfaccia applicativa che supporti tutti i comportamenti che i requisiti mi avevano espresso; faccio notare che, volutamente, manca l'ordinamento, perché questa sarà una sorpresa che gestiremo attraverso il <code>ServiceTracker</code> più avanti.</p> <p>Adesso invece passiamo a vedere com'è fatta la <code>WizardChain</code>, che vi ricordo essere l'oggetto che rappresenta tutti gli anelli della catena del nostro wizard.</p> <pre> package it.dvel.playground.wizard.chain; import com.liferay.osgi.service.tracker.collections.list.ServiceTrackerList; import com.liferay.osgi.service.tracker.collections.list.ServiceTrackerListFactory; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.GetterUtil; import org.osgi.framework.BundleContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import java.util.LinkedList; @Component( immediate = true, service = WizardChain.class ) public class WizardChain extends LinkedList<WizardController> { public WizardChain() { } public void init() { // Ogni volta che reinizializzo la chain, prima la svuoto.. if (size() > 0) { removeRange(0, size()); } // poi la ripopolo... for (WizardController controller : _wizardControllers) { if (_log.isDebugEnabled()) _log.debug("Addin' controller named " + controller.getClass().getCanonicalName() + " to wizard"); add(controller); } } @Activate protected void activate(BundleContext bundleContext) { _wizardControllers = ServiceTrackerListFactory.open( bundleContext, WizardController.class, (o1, o2) -> { if (GetterUtil.getLong(o1.getProperty("dvel.wizard.screen.number"), 10) < GetterUtil.getLong(o2.getProperty("dvel.wizard.screen.number"), 10)) { return -1; } else { return 1; } }); } @Deactivate protected void deactivate() { if (_wizardControllers != null) { _wizardControllers.close(); } } private ServiceTrackerList <WizardController, WizardController> _wizardControllers; public static final Log _log = LogFactoryUtil.getLog(WizardChain.class); }</pre> <p>Qui abbiamo già svelato buona parte della magia!</p> <p>Come potrete notare, infatti, qui sono risolti:</p> <ul> <li>il "segreto" del <code>ServiceTracker</code>;</li> <li>il "segreto" del sorting dei servizi.</li> </ul> <p>Ma vediamo nel dettaglio quanto sopra, così da coglierne i punti interessanti.</p> <p>Partiamo subito facendo notare che qui stiamo creando tecnicamente un <code>Component</code>; questo aspetto è importante perché trasforma, di fatto, il nostro oggetto in un <em>servizio OSGi</em> che potremo in futuro -volendo- sovrascrivere. Su questo tema si sono spese in questi anni miliardi di parole / righe di codice, quindi soprassiedo perché non credo sia importante. Se per qualcuno lo fosse, scrivetelo nei commenti che poi lo spiego! ;)</p> <p>Il secondo aspetto è che per l'implementazione della mia chain ho scelto -non a caso- di estendere una <code>LinkedList</code>; anche questo aspetto è voluto: siccome pensavo potesse tornarmi comodo gestire la catena a partire da un singolo anello; ho pensato potesse essere comodo avere una struttura dati che rappresentasse, appunto, il concetto di "lista di elementi tra loro collegati".</p> <p>Ho quindi scelto di utilizzare una <code>LinkedList</code>, che fa esattamente quello che volevo! ;)</p> <p>Se saltiamo in fondo alla classe, poi, troveremo, subito prima della definizione del classico <code>Log</code> (<strong>digressione</strong>: ancora non mi capacito come, per uno che ha scritto 10milioni di righe di codice praticamente da solo, una costante <code>static</code> e <code>final</code> si chiami convenzionalmente con il nome di un field interno di classe, ma tant'è..), troviamo questo <em>esoterico</em> pezzo di codice:</p> <pre> private ServiceTrackerList <WizardController, WizardController> _wizardControllers;</pre> <p>Da un punto di vista implementativo, questa è la prima magia!</p> <p>Questa è di fatto una implementazione di ServiceTracker fornita out-of-the-box direttamente da Liferay.</p> <p>Io ero partito pensando di scrivermi, come suggerito dalla documentazione che cito all'inizio del post ;), un <code>ServiceTracker</code> custom; poi ravanando nel codice sorgente di Liferay 7.1, ho scoperto il mondo dei <code>ServiceTracker</code> già implementati e il gioco è stato semplice! Anche perché, ho pensato, vuoi che il buon Brian non abbia pensato e me e non mi abbia già messo a disposizione tutto quello che mi serve?? Infatti è così: come sempre lavorare con Liferay, se conosci bene un milione di framework / concetti / infastruttura / codice / quello-che-preferisci è davvero piacevole!! C'è sicuramente già tutto quello che ti serve.. Se sai dove andarlo a cercare!! :D</p> <p>Ma torniamo alla nostra implementazione! Dicevo.. Mi sono scelto quello che faceva di più al caso mio (quindi una lista di Service e non, banalmente, un Singleton) e l'ho utilizzata!</p> <div class="portlet-msg-info"><strong>Hint!</strong> Se anche voi siete curiosi di vedere quali e quanti ServiceTracker Liferay 7.1 vi mette a disposizione OOTB, vi consiglio di guardare il contenuto del package <code>com.liferay.osgi.service.tracker.collections.list</code> e dare un occhio attento anche alle varie Factory sparse al suo interno.. ;)</div> <p>Arrivati a questo punto, però, vi devo spiegare un attimino (moooolto a grandi linee) come funziona il cinema dei <code>ServiceTracker</code>, lato OSGi, altrimenti il resto dell'implementazione non è molto chiara!</p> <p>Allora, il giro prevede che tu possa <em>aprire</em> e <em>chiudere </em>una sorta di listener sul registry OSGi all'interno del quale sarà poi aggiunto / rimosso dinamicamente dal motore stesso un singolo servizio. Per fare questa magia, ovviamente, utilizziamo una factory messa a disposizione da Liferay che ci consente di nascondere tutto il codice ridondante che ci vorrebbe per istanziare un <code>ServiceTracker</code> e che ci permette, invece, di ottenerlo molto più velocemente e con un dispendio di codice quasi pari a zero.</p> <p>A questo proposito, quindi, utilizzo per inizializzare il nostro <code>ServiceTracker</code> l'oggetto <code>ServiceTrackerList</code> questa chiamata:</p> <pre> _wizardControllers = ServiceTrackerListFactory.open( bundleContext, WizardController.class, (o1, o2) -> { if (GetterUtil.getLong(o1.getProperty("dvel.wizard.screen.number"), 10) < GetterUtil.getLong(o2.getProperty("dvel.wizard.screen.number"), 10)) { return -1; } else { return 1; } });</pre> <p>Questa è un po' da argomentare anche se, immagino, i più sgami di voi avranno già intuito tutto! :)</p> <p>Allora, la <code>ServiceTrackerListFactory</code>, attraverso il metodo statico <code>open()</code> ci permette, appunto, di ottenere una reference al <code>ServiceTracker</code> che ci serve. <code>ServiceTracker</code> che, faccio notare, è <strong>tipato</strong> sul Servizio che vogliamo ascoltare.</p> <p>E nel nostro caso specifico, il <code>ServiceTracker</code> che ci serve è, ovviamente un <code>ServiceTrackerList</code> (infatti lo istanziamo una <code>ServiceTrackerListFactory</code>.. :)) ma, al metodo <code>open()</code>, specifichiamo:</p> <ul> <li>il <code>bundleContext</code> nel quale siamo;</li> <li>il tipo di servizi che il <code>ServiceTracker</code> deve ascoltare (nel nostro caso, appunto, i <code>WizardController</code>);</li> <li>il metodo di <em>sort</em> per ordinarli che, udite udite ;), IO ho scelto di implementare con una <strong>lambda</strong> (lo so: <strong>io ero più incredulo di voi</strong>, ma quando ho capito che la <strong>parallel execution</strong> è a un passo da me ho capito che potevo fidarmi anche io delle lambda.. ;))</li> </ul> <p>Nello specifico, tutto questo avviene all'interno di un metodo annotato con una annotation che è <code>Activate</code> che, nemmeno a dirlo, permette di far invocare il metodo annotato <strong>all'attivazione</strong> del <code>bundle</code>.</p> <div class="portlet-msg-info">Si: è un modo alternativo per definire il bundle activator <strong>senza definirlo esplicitamente</strong> nel descrittore OSGi..</div> <p>La lambda comunque è semplice: mi aspetto che ogni Servizio che ricevo abbia tra le sue properties la property <code>dvel.wizard.screen.number</code> sulla quale, poi, faccio il classico sort utilizzando la logica del <code>Comparator</code>. In questo modo riesco a risolvere anche il requisito numero tre, ovvero quello <strong>dell'ordinabilità dei componenti</strong> all'interno del wizard sempre, requisito numero uno, in maniera <strong>disaccoppiata</strong>.</p> <p>E il bello è che la cosa, grazie alle facility messe a disposizione dagli oggetti di Liferay, è semplice come.. Passare una lambda che implementa il <code>Comparator</code>! :)</p> <p>A questo punto il più è fatto: abbiamo una <em>reference</em> interna al nostro <em>service</em> che è la chain che viene mantenuto costantemente aggiornato dal motore OSGi, a <strong>runtime</strong>, e nel quale vengono aggiunti / rimossi i servizi che stiamo <em>ascoltando</em> senza che noi si debba fare nulla.</p> <p>L'unica accortezza che dobbiamo avere, pena una tediosissima <em>memory leak</em> legata alla non <em>garbadgiabilità</em> della classe ;), è quella di chiudere il listener quando il modulo viene rimosso / spento / disattivato dal container; lo facciamo, banalmente, qui:</p> <pre> @Deactivate protected void deactivate() { if (_wizardControllers != null) { _wizardControllers.close(); } }</pre> <p>Non credo ci sia bisogno di commentare questo codice, quindi su questo soprassiedo.</p> <p>Ultimo pezzo da vedere, l'implementazione della Portlet che poi piloterà tutto il giro!</p> <p>Ed ecco qui l'ultimo pezzo del nostro trittico:</p> <pre> package it.dvel.playground.wizard.portlet.portlet; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.ServiceContextFactory; import com.liferay.portal.kernel.service.UserLocalService; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import it.dvel.playground.util.PortletKeys; import it.dvel.playground.wizard.chain.WizardChain; import it.dvel.playground.wizard.chain.WizardController; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import javax.portlet.*; import java.io.IOException; /** * @author jed */ @Component( immediate = true, property = { "com.liferay.portlet.display-category=category.dvel.playground", "com.liferay.portlet.instanceable=false", "javax.portlet.init-param.template-path=/", "javax.portlet.init-param.view-template=/view.jsp", "javax.portlet.name=" + PortletKeys.WIZARD, "javax.portlet.display-name=" + PortletKeys.WIZARD_DISPLAY_NAME, "javax.portlet.resource-bundle=content.Language", "javax.portlet.security-role-ref=power-user,user" }, service = Portlet.class ) public class WizardPortlet extends MVCPortlet { public void processLogic(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException { WizardController controller = getWizardController(actionRequest); if (Validator.isNotNull(controller)) { controller.processLogic(actionRequest, actionResponse); } } private WizardController getWizardController(PortletRequest request) throws PortletException { try { ServiceContext ctx = ServiceContextFactory.getInstance(request); User user = userLocalService.getUser(ctx.getUserId()); chain.init(); for (int i = 0; i < chain.size(); i++) { WizardController controller = chain.get(i); if (controller.isValid(user) && !controller.isCompleted(user, ctx)) { return controller; } } return null; } catch (PortalException e) { throw new PortletException(e.getMessage(), e); } } @Override public void render(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { WizardController controller = getWizardController(renderRequest); if (Validator.isNotNull(controller)) { include(controller.getJspPath(), renderRequest, renderResponse); } else { renderRequest.setAttribute(WebKeys.PORTLET_CONFIGURATOR_VISIBILITY, Boolean.FALSE); include(viewTemplate, renderRequest, renderResponse); } } @Reference private UserLocalService userLocalService; @Reference private WizardChain chain; public static final Log _log = LogFactoryUtil.getLog(WizardPortlet.class); }</pre> <p>Come immagino vi sareste aspettati, qui non c'è nessuna magia:</p> <ul> <li>creo un <code>Component</code> di tipo <code>Portlet</code> (maddai.. :))</li> <li>mi faccio iniettare una reference della mia <code>WizardChain</code> (così che in future release io possa sovrascriverla agilmente via OSGi)</li> <li>mappo i metodi della mia <code>Portlet</code> (<code>render()</code> e <code>processAction()</code>) sui metodi dei singoli <code>WizardController</code>, così da poter gestire tutto il processo in modalità <strong>disaccoppiata</strong>.</li> </ul> <p>Unico dettaglio implementativo: quando l'utente ha completato il wizard, il requisito era che la portlet non fosse nemmeno visibile sulla pagina; questo spiega perché nel <code>render()</code> c'è questa implementazione: </p> <pre> if (Validator.isNotNull(controller)) { include(controller.getJspPath(), renderRequest, renderResponse); } else { renderRequest.setAttribute(WebKeys.PORTLET_CONFIGURATOR_VISIBILITY, Boolean.FALSE); include(viewTemplate, renderRequest, renderResponse); }</pre> <p>Di fatto, se ho un controller delego ad esso la view altrimenti, come da requisito, nascondo la portlet!</p> <p>Direi che questo è tutto, ora tocca a voi! Vi è mai capitato di avere a che fare con <em>animali mitologici</em> come i miei? :) Scontrarvi con requisiti funzionali di questo tipo o, più in generale, avete mai avuto bisogno di scomodare un <code>ServiceTracker</code>?</p> <p>Oppure, più semplicemente, voler scrivere codice molto più riusabile, indipendente e disaccoppiato rispetto al classico monolite??</p> <p>Nel caso in cui a una di queste domande abbiate risposto si, beh, ora avete una strada da investigare per vedere di riuscire a divertirvi mentre fate ciò che dovete con Liferay!</p> <p>Come sempre, rimango in attesa di avere i vostri feedback; soprattutto sull'implementazione.</p> <p>Confesso che ci sono cose sulle quali anche io sono stato titubante e, in partenza, avevo pensato di modellarle diversamente. Poi per tutta una serie di ragioni (la prima.. Il tempo!! :)), ho preferito implementare le cose così; però, se voi a differenza di me avete più tempo e voglia di regalarmi i vostri 2c, sarò ben felice di ascoltare un punto di vista differente!</p> <p>Fino ad allora, come si dice in questi casi, happy coding e.. Lunga vita ai <code>ServiceTracker</code>!!! :D</p> <p>Alla prossima, ciao, J.</p>Jader Jed Francia2019-04-30T15:41:00ZIndentazione dei paragrafi con Alloy EditorMarco Napolitanohttps://blog.d-vel.com/home/-/blogs/indentazione-dei-paragrafi-con-alloy-editor2019-04-29T07:00:28Z2019-04-29T07:00:00Z<p>In un <a href="http://blog.d-vel.com/home/-/blogs/apice-e-pedice-con-alloy-editor">precedente articolo</a> vi ho mostrato come fare ad aggiungere i pulsanti per trasformare il testo in apice e pedice all'interno di Alloy Editor; sfruttando lo stesso principio vi mostrerò ora come aggiungere i pulsanti per indentare i paragrafi.</p> <p>Questa funzionalità vi sarà molto utile soprattutto se dovete realizzare degli elenchi innestati.</p> <p>Non vi allego immagini nè vi spiegherò la teoria, ma vi farò vedere solamente il codice finale; se siete curiori leggete il precedente articolo.</p> <pre> @Component(property = { "editor.name=alloyeditor", "javax.portlet.name=" + JournalPortletKeys.JOURNAL, "service.ranking:Integer=100" }, service = EditorConfigContributor.class) public class TextAlignmentEditorConfigContributor extends BaseEditorConfigContributor { @Override public void populateConfigJSONObject( JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes, ThemeDisplay themeDisplay, RequestBackedPortletURLFactory requestBackedPortletURLFactory) { JSONObject toolbars = jsonObject.getJSONObject("toolbars"); if (toolbars != null) { JSONObject toolbarStyles = toolbars.getJSONObject("styles"); if (toolbarStyles != null) { JSONArray selections = toolbarStyles.getJSONArray("selections"); if (selections != null) { for (int i = 0; i < selections.length(); i++) { JSONObject selection = selections.getJSONObject(i); if (selection.has("name") && selection.getString("name").equals("text")) { JSONArray buttons = selection.getJSONArray("buttons"); if (buttons != null) { buttons.put("indentBlock"); buttons.put("outdentBlock"); } break; } } } } } } }</pre>Marco Napolitano2019-04-29T07:00:00ZApice e pedice con Alloy EditorMarco Napolitanohttps://blog.d-vel.com/home/-/blogs/apice-e-pedice-con-alloy-editor2019-04-23T07:00:28Z2019-04-23T07:00:00Z<p>In un <a href="http://blog.d-vel.com/home/-/blogs/allineamento-del-testo-con-alloy-editor">precedente articolo</a> vi ho mostrato come fare ad aggiungere i pulsanti per l'allineamento del testo all'interno di Alloy Editor; sfruttando lo stesso principio vi mostrerò ora come aggiungere i pulsanti per trasformare il testo in apice e pedice.</p> <p>Non vi allego immagini nè vi spiegherò la teoria, ma vi farò vedere solamente il codice finale; se siete curiori leggete il precedente articolo.</p> <pre> @Component(property = { "editor.name=alloyeditor", "javax.portlet.name=" + JournalPortletKeys.JOURNAL, "service.ranking:Integer=100" }, service = EditorConfigContributor.class) public class TextAlignmentEditorConfigContributor extends BaseEditorConfigContributor { @Override public void populateConfigJSONObject( JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes, ThemeDisplay themeDisplay, RequestBackedPortletURLFactory requestBackedPortletURLFactory) { JSONObject toolbars = jsonObject.getJSONObject("toolbars"); if (toolbars != null) { JSONObject toolbarStyles = toolbars.getJSONObject("styles"); if (toolbarStyles != null) { JSONArray selections = toolbarStyles.getJSONArray("selections"); if (selections != null) { for (int i = 0; i < selections.length(); i++) { JSONObject selection = selections.getJSONObject(i); if (selection.has("name") && selection.getString("name").equals("text")) { JSONArray buttons = selection.getJSONArray("buttons"); if (buttons != null) { buttons.put("superscript"); buttons.put("subscript"); } break; } } } } } } }</pre>Marco Napolitano2019-04-23T07:00:00Z