| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| OctaveEngine |
|
| 1.2941176470588236;1.294 | ||||
| OctaveEngine$1 |
|
| 1.2941176470588236;1.294 | ||||
| OctaveEngine$2 |
|
| 1.2941176470588236;1.294 |
| 1 | /* | |
| 2 | * Copyright 2008, 2010 Ange Optimization ApS | |
| 3 | * | |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 | * you may not use this file except in compliance with the License. | |
| 6 | * You may obtain a copy of the License at | |
| 7 | * | |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | * | |
| 10 | * Unless required by applicable law or agreed to in writing, software | |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 | * See the License for the specific language governing permissions and | |
| 14 | * limitations under the License. | |
| 15 | */ | |
| 16 | /** | |
| 17 | * @author Kim Hansen | |
| 18 | */ | |
| 19 | package eu.simuline.octave; | |
| 20 | ||
| 21 | import java.io.File; | |
| 22 | import java.io.IOException; | |
| 23 | import java.io.OutputStreamWriter; | |
| 24 | import java.io.Reader; | |
| 25 | import java.io.StringReader; | |
| 26 | import java.io.StringWriter; | |
| 27 | import java.io.Writer; | |
| 28 | ||
| 29 | import java.util.Collections; | |
| 30 | import java.util.Map; | |
| 31 | import java.util.Random; | |
| 32 | ||
| 33 | import eu.simuline.octave.exception.OctaveEvalException; | |
| 34 | import eu.simuline.octave.exception.OctaveClassCastException; | |
| 35 | import eu.simuline.octave.exception.OctaveIOException; | |
| 36 | import eu.simuline.octave.exec.OctaveExec; | |
| 37 | import eu.simuline.octave.exec.ReadFunctor; | |
| 38 | import eu.simuline.octave.exec.ReaderWriteFunctor; | |
| 39 | import eu.simuline.octave.exec.WriteFunctor; | |
| 40 | import eu.simuline.octave.exec.WriterReadFunctor; | |
| 41 | import eu.simuline.octave.io.OctaveIO; | |
| 42 | import eu.simuline.octave.type.OctaveObject; | |
| 43 | import eu.simuline.octave.type.OctaveString; | |
| 44 | import eu.simuline.octave.type.cast.Cast; | |
| 45 | ||
| 46 | /** | |
| 47 | * The connection to an octave process. | |
| 48 | * | |
| 49 | * This is inspired by the javax.script.ScriptEngine interface. | |
| 50 | */ | |
| 51 | public final class OctaveEngine { | |
| 52 | ||
| 53 | // ER: nowhere used except in method getFactory() | |
| 54 | // which is in turn nowhere used. | |
| 55 | private final OctaveEngineFactory factory; | |
| 56 | ||
| 57 | /** | |
| 58 | * The executor of this octave engine. | |
| 59 | */ | |
| 60 | private final OctaveExec octaveExec; | |
| 61 | ||
| 62 | private final OctaveIO octaveIO; | |
| 63 | ||
| 64 | /** | |
| 65 | * The writer to write to stdout. | |
| 66 | */ | |
| 67 | 106 | private Writer writer = new OutputStreamWriter(System.out, |
| 68 | 106 | OctaveUtils.getUTF8()); |
| 69 | ||
| 70 | /** | |
| 71 | * Describe variable <code>random</code> here. | |
| 72 | * | |
| 73 | */ | |
| 74 | 106 | private final Random random = new Random(); |
| 75 | ||
| 76 | /** | |
| 77 | * Creates an octave engine with the given parameters. | |
| 78 | * The first one is nowhere used and the others are handed over to | |
| 79 | * {@link OctaveExec#OctaveExec(int,Writer,Writer,String[],String[],File)}. | |
| 80 | */ | |
| 81 | OctaveEngine(final OctaveEngineFactory factory, | |
| 82 | final int numThreadsReuse, | |
| 83 | final Writer octaveInputLog, | |
| 84 | final Writer errorWriter, | |
| 85 | final String[] cmdArray, | |
| 86 | final String[] environment, // always invoked with null | |
| 87 | 106 | final File workingDir) { |
| 88 | 106 | this.factory = factory; |
| 89 | // assert environment == null; | |
| 90 | ||
| 91 | 106 | this.octaveExec = new OctaveExec(numThreadsReuse, |
| 92 | octaveInputLog, | |
| 93 | errorWriter, | |
| 94 | cmdArray, | |
| 95 | environment, | |
| 96 | workingDir); | |
| 97 | 106 | this.octaveIO = new OctaveIO(this.octaveExec); |
| 98 | 106 | } |
| 99 | ||
| 100 | /** | |
| 101 | * Returns the according read functor: | |
| 102 | * If {@link #writer} is non-null, | |
| 103 | * wrap it into a {@link WriterReadFunctor}. | |
| 104 | * Otherwise, create functor from a reader | |
| 105 | * which reads empty, i.e. without action, as long as the reader is empty. | |
| 106 | * | |
| 107 | */ | |
| 108 | @SuppressWarnings({"checkstyle:visibilitymodifier", | |
| 109 | "checkstyle:magicnumber"}) | |
| 110 | private ReadFunctor getReadFunctor() { | |
| 111 | 850 | if (this.writer == null) { |
| 112 | // If writer is null create a "do nothing" functor | |
| 113 | 4 | return new ReadFunctor() { |
| 114 | 4 | private final char[] buffer = new char[4096]; |
| 115 | ||
| 116 | @Override | |
| 117 | @SuppressWarnings("checkstyle:emptyblock") | |
| 118 | public void doReads(final Reader reader) throws IOException { | |
| 119 | 10 | while (reader.read(buffer) != -1) { // NOPMD |
| 120 | // Do nothing | |
| 121 | } | |
| 122 | 4 | } |
| 123 | }; | |
| 124 | } else { | |
| 125 | 846 | return new WriterReadFunctor(this.writer); |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | /** | |
| 130 | * Execute the given script. | |
| 131 | * | |
| 132 | * @param script | |
| 133 | * the script to execute | |
| 134 | * @throws OctaveIOException | |
| 135 | * if the script fails, this will kill the engine | |
| 136 | */ | |
| 137 | public void unsafeEval(final Reader script) { | |
| 138 | 4 | this.octaveExec.evalRW(new ReaderWriteFunctor(script), |
| 139 | 2 | getReadFunctor()); |
| 140 | 2 | } |
| 141 | ||
| 142 | // ER: see also {@link #eval(final String script)} | |
| 143 | /** | |
| 144 | * Execute the given script. | |
| 145 | * | |
| 146 | * @param script | |
| 147 | * the script to execute | |
| 148 | * @throws OctaveIOException | |
| 149 | * if the script fails, this will kill the engine | |
| 150 | */ | |
| 151 | public void unsafeEval(final String script) { | |
| 152 | 1696 | this.octaveExec.evalRW(new WriteFunctor() { |
| 153 | @Override | |
| 154 | public void doWrites(final Writer writer2) throws IOException { | |
| 155 | 848 | writer2.write(script); |
| 156 | 848 | } |
| 157 | }, | |
| 158 | 848 | getReadFunctor()); |
| 159 | 840 | } |
| 160 | ||
| 161 | // ER: | |
| 162 | // based on {@link #unsaveEval(final String script)} | |
| 163 | // in contrast to {@link #unsaveEval(final String script)} | |
| 164 | // errors are caught. | |
| 165 | // Implementation is based on octave Built-in Function eval (try, catch) | |
| 166 | // both try and catch being strings. | |
| 167 | // try is always evaluated and catch is evaluated in case of an error | |
| 168 | // while evaluating try. | |
| 169 | // The last error ist returned by built/in function lasterr(). | |
| 170 | // | |
| 171 | // evaluates 'eval(javaoctave_X_eval,"javaoctave_X_lasterr = lasterr();");' | |
| 172 | // where javaoctave_X_eval is a variable containing script as a string | |
| 173 | // and X is some random number | |
| 174 | // | |
| 175 | // That way, in case of an error, | |
| 176 | // javaoctave_X_lasterr contains the string representtion of this error. | |
| 177 | /** | |
| 178 | * A safe eval that will not break the engine on syntax errors | |
| 179 | * or other errors. | |
| 180 | * | |
| 181 | * @param script | |
| 182 | * the script to execute | |
| 183 | * @throws OctaveEvalException | |
| 184 | * if the script fails | |
| 185 | */ | |
| 186 | @SuppressWarnings("checkstyle:magicnumber") | |
| 187 | public void eval(final String script) { | |
| 188 | 844 | final String tag = String.format("%06x%06x", |
| 189 | 422 | this.random.nextInt(1 << 23), |
| 190 | 422 | this.random.nextInt(1 << 23)); |
| 191 | 422 | put(String.format("javaoctave_%1$s_eval", tag), |
| 192 | new OctaveString(script)); | |
| 193 | // Does not use lasterror() as that returns data in a matrix struct, | |
| 194 | // we can not read that yet | |
| 195 | 422 | unsafeEval(String.format("eval(javaoctave_%1$s_eval, " + |
| 196 | "\"javaoctave_%1$s_lasterr = lasterr();\");", | |
| 197 | tag)); | |
| 198 | 420 | final OctaveString lastError = |
| 199 | 420 | get(OctaveString.class, |
| 200 | 420 | String.format("javaoctave_%1$s_lasterr", tag)); |
| 201 | 420 | unsafeEval(String.format("clear javaoctave_%1$s_eval " + |
| 202 | "javaoctave_%1$s_lasterr", tag)); | |
| 203 | 420 | if (lastError != null) { |
| 204 | 2 | throw new OctaveEvalException(lastError.getString()); |
| 205 | } | |
| 206 | 418 | } |
| 207 | ||
| 208 | /** | |
| 209 | * Sets a value in octave. | |
| 210 | * | |
| 211 | * @param key | |
| 212 | * the name of the variable | |
| 213 | * @param value | |
| 214 | * the value to set | |
| 215 | */ | |
| 216 | public void put(final String key, final OctaveObject value) { | |
| 217 | 500 | this.octaveIO.set(Collections.singletonMap(key, value)); |
| 218 | 496 | } |
| 219 | ||
| 220 | /** | |
| 221 | * Sets all the mappings in the specified map as variables in octave. | |
| 222 | * These mappings replace any variable | |
| 223 | * that octave had for any of the keys currently in the specified map. | |
| 224 | * | |
| 225 | * @param vars | |
| 226 | * the variables to be stored in octave | |
| 227 | */ | |
| 228 | public void putAll(final Map<String, OctaveObject> vars) { | |
| 229 | 4 | this.octaveIO.set(vars); |
| 230 | 4 | } |
| 231 | ||
| 232 | /** | |
| 233 | * @param key | |
| 234 | * the name of the variable | |
| 235 | * @return the value from octave or null if the variable does not exist | |
| 236 | */ | |
| 237 | public OctaveObject get(final String key) { | |
| 238 | 600 | return this.octaveIO.get(key); |
| 239 | } | |
| 240 | ||
| 241 | /** | |
| 242 | * @param castClass | |
| 243 | * Class to cast to | |
| 244 | * @param key | |
| 245 | * the name of the variable | |
| 246 | * @param <T> | |
| 247 | * the class of the return value | |
| 248 | * @return shallow copy of value for this key, or null if key isn't there. | |
| 249 | * @throws OctaveClassCastException | |
| 250 | * if the object can not be cast to a castClass | |
| 251 | */ | |
| 252 | public <T extends OctaveObject> T get(final Class<T> castClass, | |
| 253 | final String key) { | |
| 254 | 562 | return Cast.cast(castClass, get(key)); |
| 255 | } | |
| 256 | ||
| 257 | // ER: nowhere used | |
| 258 | /** | |
| 259 | * @return the factory that created this object | |
| 260 | */ | |
| 261 | public OctaveEngineFactory getFactory() { | |
| 262 | 0 | return this.factory; |
| 263 | } | |
| 264 | ||
| 265 | /** | |
| 266 | * Set the writer that the scripts output will be written to. | |
| 267 | * | |
| 268 | * This method is usually placed in ScriptContext. | |
| 269 | * | |
| 270 | * @param writer | |
| 271 | * the writer to set | |
| 272 | */ | |
| 273 | public void setWriter(final Writer writer) { | |
| 274 | 18 | this.writer = writer; |
| 275 | 18 | } |
| 276 | ||
| 277 | /** | |
| 278 | * Set the writer that the scripts error output will be written to. | |
| 279 | * | |
| 280 | * This method is usually placed in ScriptContext. | |
| 281 | * | |
| 282 | * @param errorWriter | |
| 283 | * the errorWriter to set | |
| 284 | */ | |
| 285 | public void setErrorWriter(final Writer errorWriter) { | |
| 286 | 2 | this.octaveExec.setErrorWriter(errorWriter); |
| 287 | 2 | } |
| 288 | ||
| 289 | /** | |
| 290 | * Close the octave process in an orderly fashion. | |
| 291 | */ | |
| 292 | public void close() { | |
| 293 | 108 | this.octaveExec.close(); |
| 294 | 94 | } |
| 295 | ||
| 296 | /** | |
| 297 | * Kill the octave process without remorse. | |
| 298 | */ | |
| 299 | public void destroy() { | |
| 300 | 12 | this.octaveExec.destroy(); |
| 301 | 12 | } |
| 302 | ||
| 303 | /** | |
| 304 | * Return the version of the octave implementation. | |
| 305 | * E.g. a string like "3.0.5" or "3.2.3". | |
| 306 | * | |
| 307 | * @return Version of octave | |
| 308 | */ | |
| 309 | public String getVersion() { | |
| 310 | 8 | final StringWriter version = new StringWriter(); |
| 311 | 8 | StringReader reader = |
| 312 | new StringReader("printf(\"%s\", OCTAVE_VERSION());"); | |
| 313 | 8 | this.octaveExec.evalRW(new ReaderWriteFunctor(reader), |
| 314 | new WriterReadFunctor(version)); | |
| 315 | 8 | return version.toString(); |
| 316 | } | |
| 317 | } |