]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.graph.impl/src/org/simantics/browsing/ui/graph/impl/GraphStringIndexModifier.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.browsing.ui.graph.impl / src / org / simantics / browsing / ui / graph / impl / GraphStringIndexModifier.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.browsing.ui.graph.impl;\r
13 \r
14 import java.util.concurrent.Semaphore;\r
15 \r
16 import org.simantics.browsing.ui.BuiltinKeys;\r
17 import org.simantics.browsing.ui.NodeContext;\r
18 import org.simantics.browsing.ui.content.Labeler.Modifier;\r
19 import org.simantics.db.ReadGraph;\r
20 import org.simantics.db.Resource;\r
21 import org.simantics.db.Session;\r
22 import org.simantics.db.WriteGraph;\r
23 import org.simantics.db.common.request.ReadRequest;\r
24 import org.simantics.db.common.request.WriteRequest;\r
25 import org.simantics.db.exception.DatabaseException;\r
26 import org.simantics.db.layer0.adapter.StringIndexModifier;\r
27 import org.simantics.db.layer0.adapter.StringModifier;\r
28 import org.simantics.db.layer0.adapter.TObjectIntPair;\r
29 import org.simantics.layer0.utils.representation.StringRepresentation2;\r
30 import org.simantics.utils.datastructures.Callback;\r
31 import org.simantics.utils.ui.ErrorLogger;\r
32 \r
33 /**\r
34  * Please implement:\r
35  * <ul>\r
36  * <li>{@link #createModifierInput(String)} - constructs an input for\r
37  * {@link org.simantics.db.layer0.adapter.Modifier#modify(WriteGraph, Object)}\r
38  * from the specified label given by the user.\r
39  * <li>{@link #doModify(WriteGraph, String)} - perform the requested modification\r
40  * into the graph.</li>\r
41  * </ul>\r
42  * \r
43  * <p>\r
44  * Other points of customization:\r
45  * </p>\r
46  * <ul>\r
47  * <li>{@link #getInitialValue(ReadGraph)} - returns the value that should be shown\r
48  * initially when editing. The default implementation just adapts the input to\r
49  * its String representation, but you may want to customize this.</li>\r
50  * <li>{@link #initializeGraphModifier(ReadGraph)} - allows you to perform custom\r
51  * initialization of the modifier which uses the graph</li>\r
52  * <li>{@link #getResourceToModify()} - allows you to customize the way in which\r
53  * the input INodeContext is resolved into a Resource. The default\r
54  * implementation uses the IAdaptable interface of INodeContext to get the\r
55  * Resource.</li>\r
56  * <li>{@link #verifyModification(String)} - allows for last chance denial of\r
57  * the modification after the user has signalled approval of the modification.</li>\r
58  * </ul>\r
59  * \r
60  * @author Tuukka Lehtonen\r
61  * \r
62  * @param <T> the input class of the used\r
63  *        {@link org.simantics.db.layer0.adapter.Modifier}\r
64  */\r
65 public abstract class GraphStringIndexModifier implements Modifier {\r
66 \r
67     protected NodeContext        context;\r
68 \r
69     protected Session             session;\r
70 \r
71     protected int                 index;\r
72 \r
73     protected String              initialValue;\r
74 \r
75     protected StringIndexModifier modifier;\r
76 \r
77     /**\r
78      * Used to synchronize {@link #isValid(String)} and {@link #modify(String)}\r
79      * with the asynchronous modifier fetch.\r
80      */\r
81     protected Semaphore           modifierReady = new Semaphore(0);\r
82 \r
83     /**\r
84      * If <code>non-null</code>, the modifier could not be fetched, e.g. adapted\r
85      * from the specified INodeContext.\r
86      */\r
87     protected Throwable           modifierFailed;\r
88 \r
89 \r
90     /**\r
91      * @param context\r
92      * @param session\r
93      */\r
94     public GraphStringIndexModifier(NodeContext context, Session session, int index) throws DatabaseException {\r
95         this.context = context;\r
96         this.session = session;\r
97         this.index = index;\r
98 \r
99         final Resource r = getResourceToModify();\r
100         if (r == null)\r
101             throw new IllegalArgumentException("This modifier does not work for INodeContexts that are not adaptable to a Resource. The context input is: " + context.getConstant(BuiltinKeys.INPUT));\r
102 \r
103         session.syncRequest(new ReadRequest() {\r
104             @Override\r
105             public void run(ReadGraph g) throws DatabaseException {\r
106                 initialValue = getInitialValue(g);\r
107                 GraphStringIndexModifier.this.modifier = g.adapt(r, StringIndexModifier.class);\r
108                 initializeGraphModifier(g);\r
109                 modifierReady.release();\r
110             }\r
111         });\r
112 \r
113     }\r
114 \r
115     /**\r
116      * @param g\r
117      * @return the value that shall be returned by {@link #getValue()}\r
118      */\r
119     protected String getInitialValue(ReadGraph g) throws DatabaseException {\r
120         StringRepresentation2 sr = g.adapt(getResourceToModify(), StringRepresentation2.class);\r
121         String s = sr.get(g, index);\r
122         return s;\r
123     }\r
124 \r
125     /**\r
126      * Override to perform graph-based initialization actions for this modifier.\r
127      * \r
128      * @param g the graph handle\r
129      */\r
130     protected void initializeGraphModifier(ReadGraph g) {\r
131     }\r
132 \r
133     /**\r
134      * @return the Resource to modify based on the input INodeContext. This\r
135      *         resource must be adaptable to a {@link StringModifier} in order\r
136      *         for this modifier to work.\r
137      */\r
138     protected Resource getResourceToModify() {\r
139         Resource r = (Resource) context.getAdapter(Resource.class);\r
140         if (r == null)\r
141             throw new AssertionError("context.getAdapter(Resource.class) returned null");\r
142         return r;\r
143     }\r
144 \r
145     /**\r
146      * @return the modifier or <code>null</code> if the StringModifier adaption\r
147      *         has not completed yet.\r
148      */\r
149     protected StringIndexModifier getModifier() {\r
150         return modifier;\r
151     }\r
152 \r
153     @Override\r
154     public String getValue() {\r
155         return initialValue;\r
156     }\r
157 \r
158     @Override\r
159     public String isValid(String label) {\r
160         if (modifierFailed != null)\r
161             return "Could not resolve validator for this value, modification denied. Reason: " + modifierFailed.getMessage();\r
162         if (modifier == null)\r
163             // Cannot validate yet, the validator has not been resolved.\r
164             // For the time being, consider the value invalid for no\r
165             // apparent reason to the user.\r
166             return "";\r
167         TObjectIntPair<String> t = createModifierInput(label);\r
168         return modifier.isValid(t);\r
169     }\r
170 \r
171     @Override\r
172     public final void modify(String label) {\r
173         if (modifier == null) {\r
174             try {\r
175                 modifierReady.acquire();\r
176             } catch (InterruptedException e) {\r
177                 // TODO: throw exception?\r
178                 return;\r
179             }\r
180         }\r
181         if (modifierFailed != null)\r
182             // TODO: throw exception?\r
183             return;\r
184         final TObjectIntPair<String> t = createModifierInput(label);\r
185         if (!verifyModification(t))\r
186             return;\r
187         session.asyncRequest(new WriteRequest() {\r
188             @Override\r
189             public void perform(WriteGraph graph) throws DatabaseException {\r
190                 doModify(graph, t);\r
191             }\r
192         }, new Callback<DatabaseException>() {\r
193             @Override\r
194             public void run(DatabaseException parameter) {\r
195                 if (parameter != null)\r
196                     ErrorLogger.defaultLogError(parameter);\r
197             }\r
198         });\r
199     }\r
200 \r
201     /**\r
202      * Called one last time before actually performing the modifying write\r
203      * transaction to verify whether this is really desired or not.\r
204      * \r
205      * <p>\r
206      * This default implementation will always allow the modification to proceed.\r
207      * </p>\r
208      * \r
209      * @param label the label to be given to the modifier\r
210      * @return <code>true</code> to go forward with the transaction,\r
211      *         <code>false</code> to bail out\r
212      */\r
213     protected boolean verifyModification(TObjectIntPair<String> label) {\r
214         return true;\r
215     }\r
216 \r
217     public abstract void doModify(WriteGraph graph, TObjectIntPair<String> label) throws DatabaseException;\r
218 \r
219     /**\r
220      * Constructs T from the specified label which is then given to\r
221      * {@link #doModify(WriteGraph, Object)} as the class T argument.\r
222      * \r
223      * @param fromLabel the modified label specified by the user\r
224      * @return the\r
225      *         {@link org.simantics.db.layer0.adapter.Modifier#modify(WriteGraph, Object)}\r
226      *         input\r
227      */\r
228     public abstract TObjectIntPair<String> createModifierInput(String fromLabel);\r
229 \r
230 };\r