--- /dev/null
+/*****************************************************************************
+ * Copyright Igor Barma, Eric Brun, Alexandre Desoubeaux, Christian Martel,
+ * (2 décembre 2008)
+ *
+ * Ce logiciel est un programme informatique servant à l'évaluation des
+ * compétences.
+ *
+ * Ce logiciel est régi par la licence CeCILL soumise au droit français et
+ * respectant les principes de diffusion des logiciels libres. Vous pouvez
+ * utiliser, modifier et/ou redistribuer ce programme sous les conditions
+ * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
+ * sur le site "http://www.cecill.info".
+ *
+ * En contrepartie de l'accessibilité au code source et des droits de copie,
+ * de modification et de redistribution accordés par cette licence, il n'est
+ * offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
+ * seule une responsabilité restreinte pèse sur l'auteur du programme, le
+ * titulaire des droits patrimoniaux et les concédants successifs.
+ *
+ * A cet égard l'attention de l'utilisateur est attirée sur les risques
+ * associés au chargement, à l'utilisation, à la modification et/ou au
+ * développement et à la reproduction du logiciel par l'utilisateur étant
+ * donné sa spécificité de logiciel libre, qui peut le rendre complexe à
+ * manipuler et qui le réserve donc à des développeurs et des professionnels
+ * avertis possédant des connaissances informatiques approfondies. Les
+ * utilisateurs sont donc invités à charger et tester l'adéquation du
+ * logiciel à leurs besoins dans des conditions permettant d'assurer la
+ * sécurité de leurs systèmes et ou de leurs données et, plus généralement,
+ * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
+ *
+ * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
+ * pris connaissance de la licence CeCILL, et que vous en avez accepté les
+ * termes.
+ *******************************************************************************/
+package com.pentila.evalcomp.client.evaluation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.gwtext.client.core.EventObject;
+import com.gwtext.client.core.ExtElement;
+import com.gwtext.client.data.Node;
+import com.gwtext.client.dd.DragData;
+import com.gwtext.client.dd.DragDrop;
+import com.gwtext.client.widgets.Editor;
+import com.gwtext.client.widgets.event.EditorListenerAdapter;
+import com.gwtext.client.widgets.form.TextField;
+import com.gwtext.client.widgets.menu.BaseItem;
+import com.gwtext.client.widgets.menu.Item;
+import com.gwtext.client.widgets.menu.Menu;
+import com.gwtext.client.widgets.menu.event.BaseItemListenerAdapter;
+import com.gwtext.client.widgets.menu.event.MenuListenerAdapter;
+import com.gwtext.client.widgets.tree.DefaultSelectionModel;
+import com.gwtext.client.widgets.tree.DropNodeCallback;
+import com.gwtext.client.widgets.tree.TreeDragData;
+import com.gwtext.client.widgets.tree.TreeEditor;
+import com.gwtext.client.widgets.tree.TreeNode;
+import com.gwtext.client.widgets.tree.TreePanel;
+import com.gwtext.client.widgets.tree.event.TreePanelListenerAdapter;
+import com.pentila.evalcomp.client.ConnexionServer;
+import com.pentila.evalcomp.client.i18n.Messages;
+import com.pentila.evalcomp.client.palette.ReferentielPalettePanel;
+import com.pentila.evalcomp.client.plan.widget.TrashableTreePanel;
+import com.pentila.evalcomp.client.utilities.Tools;
+import com.pentila.evalcomp.domain.definition.EvaluationObject;
+import com.pentila.evalcomp.domain.definition.EvaluationSubject;
+
+// TODO: Auto-generated Javadoc
+/**
+ * The Class EvalSubTreeWidget.
+ */
+public class EvalSubTreeWidget extends TrashableTreePanel {
+
+ /** The menu eval sub. */
+ private Menu menuEvalSub;
+
+ /** The emt. */
+ private EvalManagementTab emt;
+
+ /** The read only. */
+ private boolean readOnly;
+
+ /** The tree editor. */
+ private TreeEditor treeEditor;
+
+ /** The selected node. */
+ //private TreeNode selectedNode;
+
+ /** The state editing. */
+ //protected TreeNode stateEditing = null;
+
+
+ TreeNode ctxNode;
+
+
+ /**
+ * Instantiates a new eval sub tree widget.
+ *
+ * @param emt the emt
+ * @param readOnly the read only
+ */
+ public EvalSubTreeWidget(EvalManagementTab emt, boolean readOnly) {
+ super();
+ this.readOnly = readOnly;
+ this.emt = emt;
+ this.setIconCls("iconEvalSubject32");
+
+ initializeInterface();
+
+ }
+
+ /**
+ * Delete eval ob node.
+ *
+ * @param es
+ * the es
+ * @param eo
+ * the eo
+ */
+ private void deleteEvalObNode(final EvaluationSubject es,
+ EvaluationObject eo) {
+ if (!emt.isReadOnly()){
+ AsyncCallback callback = new AsyncCallback() {
+ public void onFailure(Throwable caught) {
+ // do some UI stuff to show failure
+ System.out.println("suppression des EvaluationObject");
+ Tools.hideLoadingRegion(getId());
+ }
+
+ public void onSuccess(Object result) {
+ // tell the management screen that an ES has been modified
+
+ emt.handleDeleteEOfromES(es,
+ (Map<String, Set<Long>>) result);
+ Tools.hideLoadingRegion(getId());
+ }
+ };
+ Tools
+ .showLoadingRegion(getId(),
+ Messages.getString("MAJ"), Messages.getString("Chargement"));
+ ConnexionServer.definitionService.removeEOFromES(es.getId(),
+ eo.getId(), callback);
+ }
+ }
+
+ /**
+ * Delete eval sub.
+ *
+ * @param nodeToDelete the node to delete
+ */
+ public void deleteEvalSub(TreeNode nodeToDelete) {
+
+ if (!emt.isReadOnly() && !this.isReadOnly() && nodeToDelete != null) {
+ try {
+ if (nodeToDelete.getAttribute("deleteAble").equals("true")) {
+ if (nodeToDelete.getAttribute("nodeType").equals("evalSub")) {
+
+ final EvaluationSubject es = (EvaluationSubject) nodeToDelete
+ .getAttributeAsObject("evalSub");
+ Set<EvaluationSubject> ses = new HashSet<EvaluationSubject>();
+ ses.add(es);
+ emt.getCurrentEvalShown().removeESAll(ses);
+ nodeToDelete.remove();
+
+ deleteEvalSubNode(es.getId());
+
+ } else {
+ // cas de la suppression d un evaluation object
+ EvaluationObject eo = (EvaluationObject) nodeToDelete
+ .getAttributeAsObject("evalObject");
+ final EvaluationSubject es = (EvaluationSubject) nodeToDelete
+ .getParentNode()
+ .getAttributeAsObject("evalSub");
+
+ es.getEvaluationObjects().remove(eo);
+
+ if (es.getEvaluationObjects().isEmpty()) {
+ Set<EvaluationSubject> ses = new HashSet<EvaluationSubject>();
+ ses.add(es);
+ emt.getCurrentEvalShown().removeESAll(ses);
+ deleteEvalSubNode(es.getId());
+ ((TreeNode) nodeToDelete.getParentNode()).remove();
+ }
+
+ else {
+ ((TreeNode) nodeToDelete.getParentNode())
+ .setText(es.getComputedName());
+ nodeToDelete.remove();
+ deleteEvalObNode(es, eo);
+ }
+ }
+
+ }
+ } catch (Exception ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+
+ }
+
+ /**
+ * Delete eval sub node.
+ *
+ * @param idES
+ * the id es
+ */
+ private void deleteEvalSubNode(Long idES) {
+ if (!emt.isReadOnly() || !this.isReadOnly()){
+ Set<Long> idsES = new HashSet<Long>();
+ idsES.add(idES);
+
+ AsyncCallback callback = new AsyncCallback() {
+ public void onFailure(Throwable caught) {
+ // do some UI stuff to show failure
+ System.out.println("suppression des EvaluationSubject");
+ Tools.hideLoadingRegion(getId());
+ }
+
+ public void onSuccess(Object result) {
+ // the server return a list of entity def ids to remove from the
+ // eval definition
+ // tell the management screen that an ES has been deleted
+ emt.handleDeleteES((Set<Long>) result);
+ if (rootNode.isLeaf()) {
+ // Affiche ou non le bouton d association
+ emt.showOrHideAssociateButton();
+ emt.showOrHideEventActivityButton();
+ emt.showOrHideExtRessourceButton();
+ emt.setEvalSubInitialize(false);
+ }
+ Tools.hideLoadingRegion(getId());
+ }
+ };
+ Tools
+ .showLoadingRegion(getId(),
+ Messages.getString("MAJ"), Messages.getString("Chargement"));
+ ConnexionServer.definitionService.deleteESs(idsES, callback);
+ }
+ }
+
+
+ /**
+ * Deep clone.
+ *
+ * @param tn
+ * the tn
+ *
+ * @return the tree node
+ */
+ public TreeNode deepClone(TreeNode tn) {
+ TreeNode mtn = tn.cloneNode();
+ for (Node t : tn.getChildNodes()) {
+ mtn.appendChild(deepClone((TreeNode) t));
+ }
+ return mtn;
+ }
+
+ /**
+ * Gets the evaluation subject node.
+ *
+ * @param es
+ * the es
+ *
+ * @return the evaluation subject node
+ */
+ private TreeNode getEvaluationSubjectNode(EvaluationSubject es) {
+ TreeNode myEvalSubNode = new TreeNode();
+ myEvalSubNode.setAttribute("evalSub", es);
+ myEvalSubNode.setAttribute("nodeType", "evalSub");
+ myEvalSubNode.setAttribute("deleteAble", "true");
+ myEvalSubNode.setText(es.getComputedName());
+ myEvalSubNode.setIconCls("iconEvalSubject16");
+
+ List<EvaluationObject> listEO = new ArrayList<EvaluationObject>(es
+ .getEvaluationObjects());
+ Collections.sort(listEO, new Comparator<EvaluationObject>() {
+ public int compare(EvaluationObject eo1, EvaluationObject eo2) {
+
+ return eo1.getName().compareTo(eo2.getName());
+ }
+ });
+
+ for (EvaluationObject eo : listEO) {
+ try {
+ TreeNode child = ReferentielPalettePanel.getEvalOBJTreeNode(eo,
+ false);
+ child.setAttribute("deleteAble", "true");
+ child.setAttribute("nodeType", "evalObj");
+ myEvalSubNode.appendChild(child);
+ } catch (Exception ex) {
+
+ }
+ }
+ return myEvalSubNode;
+ }
+
+ /**
+ * Initialize interface.
+ */
+ private void initializeInterface() {
+ this.setTitle(Messages.getString("Competences_visees"));
+
+
+ rootNode.setLeaf(false);
+ rootNode.setText(Messages.getString("Groupes_de_competences"));
+ rootNode.setAttribute("nodeType", "root");
+
+
+
+ this.setRootNode(rootNode);
+ this.setWidth(220);
+ this.setHeight(300);
+ this.setLines(true);
+ this.setRootVisible(true);
+ this.setAnimate(true);
+ this.setAutoScroll(true);
+
+ if (!readOnly){
+
+
+ TextField field = new TextField();
+ field.setSelectOnFocus(true);
+ field.setAllowBlank(false);
+
+ treeEditor = new TreeEditor(this, field);
+
+
+
+
+ this.setEnableDD(true);
+ this.setDdGroup("evalCompDDGroup");
+
+
+
+ this.addListener(new TreePanelListenerAdapter() {
+
+ public void onTextChange(TreeNode treeNode, String str, String oldStr){
+
+
+ if (!emt.isReadOnly() && !str.equals(oldStr)){
+
+ renameEvalSub(str, treeNode);
+ }
+ }
+
+ /*public void onClick(TreeNode node, EventObject e) {
+
+ selectedNode = node;
+
+ }*/
+
+ // Gerer l'ouverture de l'arbre au double click
+ public void onDblClick(TreeNode node, EventObject e)
+ {
+ if (!isReadOnly()){
+ treeEditor.startEdit(node);
+
+ if(node.isExpanded()) {
+ node.collapse();
+ } else {
+ node.expand();
+ }
+ }
+
+ }
+
+
+ @Override
+ public boolean doBeforeNodeDrop(TreePanel treePanel,
+ TreeNode target, DragData dragData, String point,
+ DragDrop source, TreeNode dropNode,
+ DropNodeCallback dropDropNodeCallback) {
+
+ if (!isReadOnly() && target.getAttribute("nodeType").equals("root")) {
+ EvaluationObject myEvalObj = (EvaluationObject) dropNode
+ .getAttributeAsObject("evalObject");
+ EvaluationSubject es = new EvaluationSubject();
+ es.getEvaluationObjects().add(myEvalObj);
+
+ AsyncCallback callback = new AsyncCallback() {
+ public void onFailure(Throwable caught) {
+ // do some UI stuff to show failure
+ System.out
+ .println("Probleme dans la sauvegarde des EvaluationSubject");
+ Tools.hideLoadingRegion(getId());
+ }
+
+ public void onSuccess(Object result) {
+
+ EvaluationSubject es = (EvaluationSubject) result;
+
+ TreeNode myEvalSubNode = new TreeNode();
+ myEvalSubNode.setAttribute("deleteAble", "true");
+ myEvalSubNode.setAttribute("evalSub", es);
+ myEvalSubNode.setAttribute("nodeType", "evalSub");
+ myEvalSubNode.setText(es.getComputedName());
+
+ myEvalSubNode.setIconCls("iconEvalSubject16");
+
+ TreeNode child = ReferentielPalettePanel
+ .getEvalOBJTreeNode(
+ (EvaluationObject) es
+ .getEvaluationObjects()
+ .toArray()[0], false);
+ child.setAttribute("deleteAble", "true");
+ myEvalSubNode.appendChild(child);
+ rootNode.appendChild(myEvalSubNode);
+
+ emt.getCurrentEvalShown().getEvaluationSubjects().add(es);
+
+ // Affiche ou non le bouton d association
+ emt.showOrHideAssociateButton();
+ emt.showOrHideEventActivityButton();
+ emt.showOrHideExtRessourceButton();
+ emt.setEvalSubInitialize(true);
+
+ // tell the management screen that a new ES has been
+ // created
+ emt.handleNewES(es);
+ Tools.hideLoadingRegion(getId());
+ }
+
+ };
+ Tools
+ .showLoadingRegion(getId(),
+ Messages.getString("MAJ"), Messages.getString("Chargement"));
+ ConnexionServer.definitionService.addESToED(
+ emt.getCurrentEvalShown().getId(), es, callback);
+
+ }
+
+ if (!isReadOnly() && target.getAttribute("nodeType").equals("evalSub")) {
+ EvaluationObject myEvalObj = (EvaluationObject) dropNode
+ .getAttributeAsObject("evalObject");
+ EvaluationSubject es = (EvaluationSubject) target
+ .getAttributeAsObject("evalSub");
+
+ emt.getCurrentEvalShown().getEvaluationSubjects().remove(es);
+
+ es.getEvaluationObjects().add(myEvalObj);
+
+ AsyncCallback callback = new AsyncCallback() {
+ public void onFailure(Throwable caught) {
+ // do some UI stuff to show failure
+ System.out
+ .println("Probleme dans la sauvegarde des EvaluationSubject");
+ Tools.hideLoadingRegion(getId());
+ }
+
+ public void onSuccess(Object result) {
+ EvaluationSubject es = (EvaluationSubject) result;
+ for (Node evalSubNode : rootNode.getChildNodes()) {
+ if (((EvaluationSubject) evalSubNode
+ .getAttributeAsObject("evalSub"))
+ .getId().equals(es.getId())) {
+ TreeNode myEvalSubNode = getEvaluationSubjectNode(es);
+ rootNode.replaceChild(myEvalSubNode,
+ evalSubNode);
+ myEvalSubNode.expand();
+ }
+ }
+ emt.getCurrentEvalShown().getEvaluationSubjects().add(es);
+
+ // tell the management screen that an ES has been
+ // modified
+ emt.handleModifyES(es);
+ Tools.hideLoadingRegion(getId());
+ }
+ };
+ Tools
+ .showLoadingRegion(getId(),
+ Messages.getString("MAJ"), Messages.getString("Chargement"));
+ ConnexionServer.definitionService.addESToED(
+ emt.getCurrentEvalShown().getId(), es, callback);
+ }
+ removeClass("targetDragRed"); //$NON-NLS-1$
+ return false;
+ }
+
+ @Override
+ public void onContextMenu(TreeNode node, EventObject e) {
+ e.stopEvent();
+ try {
+ if (!isReadOnly() && node.getAttribute("deleteAble").equals("true")) {
+ showEvalSubContextMenu(e, node);
+ }
+ } catch (Exception ex) {
+ }
+ }
+
+ @Override
+ public boolean onNodeDragOver(TreePanel treePanel, TreeNode target,
+ DragData dragData, java.lang.String point, DragDrop source,
+ TreeNode dropNode) {
+ if (!isReadOnly()){
+ if (!point.equals("append")) {
+ // empeche l insertion entre deux noeuds
+ return false;
+ }
+
+ // On autorise le drop uniquement sur les evalSubjectNode et le
+ // rootNode
+ try {
+ if (!dropNode.getAttribute("isRefPalette").equals("true")) {
+ return false;
+ }
+ if (target.getAttribute("nodeType").equals("root")
+ || target.getAttribute("nodeType")
+ .equals("evalSub")) {
+ return true;
+ }
+ } catch (Exception e) {
+ }
+ }
+ return false;
+ }
+
+ });
+
+ treeEditor.setRevertInvalid(false);
+ treeEditor.addListener(new EditorListenerAdapter(){
+
+
+
+ public boolean doBeforeStartEdit(Editor source,
+ ExtElement boundEl, Object value) {
+
+ DefaultSelectionModel sm = (DefaultSelectionModel)
+ getSelectionModel();
+ TreeNode sn = sm.getSelectedNode();
+
+
+
+
+ if (sn != null && "evalSub".equals(sn.getAttribute("nodeType"))){
+
+ //stateEditing = selectedNode;
+
+
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /*public boolean doBeforeComplete(Editor source, Object value, Object startValue) {
+ if (stateEditing.equals(selectedNode)){
+ return true;
+ }
+ return false;
+ }*/
+
+ /*public void onComplete(Editor source, Object value,
+ Object startValue) {
+
+
+ System.out.println(value + " " + startValue + " "+ source );
+
+ DefaultSelectionModel sm = (DefaultSelectionModel)
+ getSelectionModel();
+ TreeNode sn = sm.getSelectedNode();
+
+
+ if (sn != null && "evalSub".equals(sn.getAttribute("nodeType"))){
+ renameEvalSub((String) value, sn);
+ //selectedNode = null;
+ }
+
+
+
+ }
+ */
+
+
+ });
+
+
+
+ setSelectionModel(new DefaultSelectionModel());
+ setAnimate(false);
+ }
+ }
+
+ /**
+ * Rename eval sub.
+ *
+ * @param text the text
+ * @param nodeToDelete the node to delete
+ */
+ private void renameEvalSub(String text, TreeNode node) {
+
+
+
+ if (!emt.isReadOnly() && node.getAttribute("nodeType").equals("evalSub")) {
+
+ EvaluationSubject es = (EvaluationSubject) node
+ .getAttributeAsObject("evalSub");
+
+ if (text != null && text.equals("")){
+ text= null;
+ }
+
+ es.setName(text);
+
+ node.setText(es.getComputedName());
+
+ AsyncCallback callback = new AsyncCallback() {
+
+ public void onFailure(Throwable arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void onSuccess(Object arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ };
+
+ ConnexionServer.definitionService.editEvaluationSubject(es,
+ callback);
+
+ }
+ }
+
+ /**
+ * Show eval sub context menu.
+ *
+ * @param e the e
+ * @param nodeToDelete the node to delete
+ */
+ private void showEvalSubContextMenu(EventObject e, final TreeNode node) {
+
+ if (menuEvalSub == null) {
+ menuEvalSub = new Menu();
+ final Item renameItem = new Item(Messages.getString("Renommer"),
+ new BaseItemListenerAdapter() {
+ @Override
+ public void onClick(BaseItem item, EventObject e) {
+ treeEditor.startEdit(ctxNode);
+ }
+ });
+
+ renameItem.setIconCls("menu-rename");
+ menuEvalSub.addItem(renameItem);
+
+ menuEvalSub.addListener(new MenuListenerAdapter() {
+ public void doBeforeShow(Menu menu) {
+
+ // Can only rename ES container not inside items
+ if ("evalSub".equals(ctxNode.getAttribute("nodeType"))) {
+ renameItem.show();
+ } else {
+ renameItem.hide();
+ }
+ }
+ });
+
+ Item deleteItem = new Item(Messages
+ .getString("Supprimer_cette_entree"),
+ new BaseItemListenerAdapter() {
+ @Override
+ public void onClick(BaseItem item, EventObject e) {
+
+ deleteEvalSub(ctxNode);
+ }
+ });
+ deleteItem.setIconCls("menu-delete");
+
+ menuEvalSub.addItem(deleteItem);
+
+ }
+
+ if (!isReadOnly()){
+ if (ctxNode != null) {
+ ctxNode = null;
+ }
+ ctxNode = node;
+ menuEvalSub.showAt(e.getXY());
+ }
+ }
+
+ /**
+ * Update eval sub tree.
+ */
+ public void updateEvalSubTree() {
+ // create source group grid
+ if (emt.getCurrentEvalShown() != null) {
+
+ List<EvaluationSubject> listES = new ArrayList<EvaluationSubject>(
+ emt.getCurrentEvalShown().getEvaluationSubjects());
+ Collections.sort(listES, new Comparator<EvaluationSubject>() {
+ public int compare(EvaluationSubject es1, EvaluationSubject es2) {
+
+ return es1.getComputedName().compareTo(
+ es2.getComputedName());
+ }
+ });
+
+ if (!emt.isReadOnly() && !listES.isEmpty()) {
+ // Affiche ou non le bouton d association
+ emt.showOrHideAssociateButton();
+ emt.showOrHideExtRessourceButton();
+ emt.showOrHideEventActivityButton();
+ emt.showOrHideConditionButton();
+ // active le lancement de l eval
+ emt.setEvalSubInitialize(true);
+ }
+
+ for (Node child : this.getRootNode().getChildNodes()){
+ child.remove();
+ }
+ for (EvaluationSubject es : listES) {
+ TreeNode myEvalSubNode = getEvaluationSubjectNode(es);
+ this.getRootNode().appendChild(myEvalSubNode);
+ }
+ this.getRootNode().expand();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.pentila.evalcomp.client.plan.widget.TrashableTreePanel#isTrashable(java.lang.Object[])
+ */
+ public boolean isTrashable(Object ...objects) {
+
+
+ if (emt.isReadOnly() || isReadOnly()){
+ return false;
+ }
+ for (Object o : objects){
+ TreeDragData myData = (TreeDragData) o;
+ TreeNode node = myData.getTreeNode();
+ try{
+ if (!node.getAttribute("deleteAble").equals("true")) {
+ return false;
+ }
+ }catch (Exception exc){};
+
+
+ }
+
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.pentila.evalcomp.client.plan.widget.TrashableTreePanel#trashedElements(java.lang.Object[])
+ */
+ public void trashedElements(Object ...objects) {
+ if (!emt.isReadOnly() && !isReadOnly()){
+ for (Object o : objects){
+ TreeDragData myData = (TreeDragData) o;
+ TreeNode node = myData.getTreeNode();
+ try{
+ if (node.getAttribute("deleteAble").equals("true")) {
+
+ this.deleteEvalSub(node);
+ emt.showOrHideAssociateButton();
+ emt.showOrHideEventActivityButton();
+ emt.showOrHideExtRessourceButton();
+
+ }
+ }catch (Exception exc){};
+
+ }
+ }
+
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+}