| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.daffodil.api |
| |
| import org.apache.daffodil.exceptions.Assert |
| import org.apache.daffodil.schema.annotation.props.gen.ParseUnparsePolicy |
| import org.apache.daffodil.util.LogLevel |
| import org.apache.daffodil.util.Logging |
| import org.apache.daffodil.util.Misc |
| import org.apache.daffodil.xml.DaffodilXMLLoader |
| |
| object DaffodilTunables { |
| |
| def apply(tunables: Map[String, String]): DaffodilTunables = { |
| apply().setTunables(tunables) |
| } |
| |
| def apply(tunable: String, value: String): DaffodilTunables = { |
| apply().setTunable(tunable, value) |
| } |
| |
| def apply(): DaffodilTunables = { |
| // override tunables from the global configuration file on the class path, if it exists |
| val (configOpt, _) = Misc.getResourceOption("/daffodil-config.xml") |
| val configTunables: Map[String, String] = |
| if (configOpt.isDefined) { |
| val loader = new DaffodilXMLLoader() |
| val node = loader.load(new URISchemaSource(configOpt.get)) |
| val trimmed = scala.xml.Utility.trim(node) |
| val tunablesNode = (trimmed \ "tunables").headOption |
| val tunablesMap: Map[String, String] = tunablesNode match { |
| case None => Map.empty |
| case Some(tunableNode) => { |
| tunableNode.child.map { n => (n.label, n.text) }.toMap |
| } |
| } |
| tunablesMap |
| } else { |
| Map.empty |
| } |
| |
| new DaffodilTunables().setTunables(configTunables) |
| } |
| } |
| |
| case class DaffodilTunables( |
| val maxSkipLengthInBytes: Long = 1024, // applicable to leadingSkip and trailingSkip |
| val maxBinaryDecimalVirtualPoint: Int = 200, // Can be as large as Int.MaxValue |
| val minBinaryDecimalVirtualPoint: Int = -200, // Can be as small as Int.MinValue |
| val generatedNamespacePrefixStem: String = "tns", |
| val readerByteBufferSize: Long = 8192, |
| // |
| // If true, require that the bitOrder property is specified. If false, use a |
| // default value for bitOrder if not defined in a schema |
| // |
| // Looks to be compile-time as it gets 'tunable' from Term. |
| // |
| val requireBitOrderProperty: Boolean = false, |
| // |
| // If true, require that the encodingErrorPolicy property is specified. If |
| // false, use a default value not defined in a schema |
| // |
| // Looks to be compile-time as it gets 'tunable' from Term. |
| // |
| val requireEncodingErrorPolicyProperty: Boolean = false, |
| // |
| // Whether to compile a schema to support parsing, unparsing, both, or to use |
| // the daf:parseUnparsePolicy from the root node. None means to use the |
| // policy from the schema, otherwise use whatever the value is |
| // |
| // Looks to be compile-time. Set in Compiler. |
| /// |
| val parseUnparsePolicy: Option[ParseUnparsePolicy] = None, |
| val maxFieldContentLengthInBytes: Long = 1024 * 1024, // Can be as large as Int.MaxValue |
| val defaultInitRegexMatchLimitInChars: Long = 32, |
| val maxDataDumpSizeInBytes: Long = 256, |
| val maxOccursBounds: Long = 1024, // Can be as large as Int.MaxValue |
| // |
| // When unexpected text is found where a delimiter is expected, this is the maximum |
| // number of bytes (characters) to display when the expected delimiter is a variable |
| // length delimiter. |
| // |
| val maxLengthForVariableLengthDelimiterDisplay: Int = 10, // will display this number of bytes |
| // |
| // In certain I/O optimized situations (text-only, encodingErrorPolicy='replace', fixed-width encoding) |
| // input files larger than this will be mmapped. Input files smaller than this |
| // will be simply read using ordinary I/O (because for small files that is just faster). |
| // This exists because mmap is more expensive than ordinary I/O for small files. |
| // |
| val inputFileMemoryMapLowThreshold: Long = 32 * 1024 * 1024, // 32Meg |
| // |
| // TODO: In the future, when we can stream and handle input larger than the JVM single |
| // object limits, input files larger than this will be streamed, i.e., using java.io.InputStream. |
| // They will not be memory mapped. A CharBuffer 2x larger may be created, and that |
| // cannot exceed the JVM maximum size, so this has to be no bigger (and perhaps quite a bit |
| // smaller) than 1/2 the maximum JVM object size. |
| // |
| // val inputFileMemoryMapHighThreshold: Long = 256 * 1024 * 1024, // 256 Meg |
| |
| // |
| // Initial array buffer size allocated for recurring elements (aka arrays) |
| // |
| // Applies to InfosetImpl, as such is a run-time thing |
| // |
| val initialElementOccurrencesHint: Long = 10, |
| val unqualifiedPathStepPolicy: UnqualifiedPathStepPolicy.Type = UnqualifiedPathStepPolicy.NoNamespace, |
| val suppressSchemaDefinitionWarnings: List[WarnID] = Nil, |
| |
| // By default, path expressions in Daffodil will only work correctly if path |
| // steps are used in an expression defined in the schema when compiled. To |
| // enable the use of other expressions (e.g. during debugging, where not all |
| // expressions are known at schema compile time), set this tunable to true. |
| // This may cause a degredation of performance in path expression evaluation, |
| // so this should be avoided when in production. This flag is automatically |
| // enabled when debugging is enabled. |
| val allowExternalPathExpressions: Boolean = false, |
| |
| // A bug exists in Java 7 that causes unexpected behavior when decode errors |
| // occur in the specific ways that Daffodil decodes data. For this reason, |
| // Daffodil throws an exception when it detects that Daffodil is not running |
| // under Java 8 or has this decoder bug. However, there are some cases where |
| // a user has no choice but to run on Java 7. Setting this tunable to false |
| // will cause Daffodil to log a warning rather than throw an exception so |
| // that a user can run Daffodil on unsupported Java versions, with the |
| // understanding that it is not fully tested and behavior may not be well |
| // defined. This boolean is experimental and should only be used by those |
| // that fully understand the risks. |
| val errorOnUnsupportedJavaVersion: Boolean = true) |
| extends Serializable |
| with Logging |
| with DataStreamLimits { |
| |
| /* Appear to be Compile-Time as the tunable is obtained from: |
| * Term, SchemaComponent, Element, etc. |
| * |
| * maxSkipLengthInBytes |
| * maxBinaryDecimalVirtualPoint |
| * minBinaryDecimalVirtualPoint |
| * requireEncodingErrorPolicyProperty |
| * requireBitOrderProperty |
| * generatedNamespacePrefixStem |
| * parseUnparsePolicy |
| * |
| * Used by StepQNameFactory. Appears to get tunable from: |
| * Expressions's DPathCompileInfo |
| * CompiledExpression's ElementRuntimeData |
| * |
| * unqualifiedPathStepPolicy |
| * |
| * Appear to be Run-Time as the tunable is obtained from: |
| * PState/UState |
| * |
| * initialElementOccurrencesHint |
| * maxOccursBounds |
| * maxDataDumpSizeInBytes |
| * |
| * Only used in Main.scala for DataInputStream: |
| * |
| * maxFieldContentLengthInBytes |
| * |
| * DataInputStream objects: |
| * |
| * defaultInitRegexMatchLimitInChars |
| * |
| * Unused? |
| * |
| * maxLengthForVariableLengthDelimiterDisplay |
| * |
| * */ |
| |
| def setTunables(tunables: Map[String, String]): DaffodilTunables = { |
| var t = this |
| tunables.foreach { case (k, v) => t = t.setTunable(k, v) } |
| t |
| } |
| |
| def setTunable(tunable: String, value: String): DaffodilTunables = { |
| tunable.toLowerCase match { |
| case "maxfieldcontentlengthinbytes" => this.copy(maxFieldContentLengthInBytes = java.lang.Long.valueOf(value)) |
| case "defaultinitialregexmatchlimitinchars" => this.copy(defaultInitRegexMatchLimitInChars = java.lang.Long.valueOf(value)) |
| case "maxdatadumpsizeinbytes" => this.copy(maxDataDumpSizeInBytes = java.lang.Long.valueOf(value)) |
| case "maxoccursbounds" => this.copy(maxOccursBounds = java.lang.Long.valueOf(value)) |
| case "maxlengthforvariablelengthdelimiterdisplay" => this.copy(maxLengthForVariableLengthDelimiterDisplay = java.lang.Integer.valueOf(value)) |
| case "inputfilememorymaplowthreshold" => this.copy(inputFileMemoryMapLowThreshold = java.lang.Long.valueOf(value)) |
| case "initialelementoccurrenceshint" => this.copy(initialElementOccurrencesHint = java.lang.Long.valueOf(value)) |
| case "unqualifiedpathsteppolicy" => { |
| val policy = value.toLowerCase match { |
| case "nonamespace" => UnqualifiedPathStepPolicy.NoNamespace |
| case "defaultnamespace" => UnqualifiedPathStepPolicy.DefaultNamespace |
| case "preferdefaultnamespace" => UnqualifiedPathStepPolicy.PreferDefaultNamespace |
| case _ => Assert.usageError("Unknown value for unqualifiedPathStepPolicy tunable. Value must be \"noNamespace\", \"defaultNamespace\", or \"perferDefaultNamespace\".") |
| } |
| this.copy(unqualifiedPathStepPolicy = policy) |
| } |
| case "requirebitorderproperty" => this.copy(requireBitOrderProperty = java.lang.Boolean.valueOf(value)) |
| case "requireencodingerrorpolicyproperty" => this.copy(requireEncodingErrorPolicyProperty = java.lang.Boolean.valueOf(value)) |
| case "maxskiplengthinbytes" => this.copy(maxSkipLengthInBytes = java.lang.Long.valueOf(value)) |
| case "maxbinarydecimalvirtualpoint" => this.copy(maxBinaryDecimalVirtualPoint = java.lang.Integer.valueOf(value)) |
| case "minbinarydecimalvirtualpoint" => this.copy(minBinaryDecimalVirtualPoint = java.lang.Integer.valueOf(value)) |
| case "generatednamespaceprefixstem" => this.copy(generatedNamespacePrefixStem = value) |
| case "readerbytebuffersize" => this.copy(readerByteBufferSize = java.lang.Long.valueOf(value)) |
| case "parseunparsepolicy" => { |
| val policy = value.toLowerCase match { |
| case "parseonly" => Some(ParseUnparsePolicy.ParseOnly) |
| case "unparseonly" => Some(ParseUnparsePolicy.UnparseOnly) |
| case "both" => Some(ParseUnparsePolicy.Both) |
| case "schema" => None |
| case _ => Assert.usageError("Unknown value for parseUnparsePolicy tunable. Value must be \"parseOnly\", \"unparseOnly\", \"both\", or \"schema\".") |
| } |
| this.copy(parseUnparsePolicy = policy) |
| } |
| case "suppressschemadefinitionwarnings" => { |
| val ws = """\s+""" |
| // value is whitespace separated list of warning identifier strings |
| val warnIDs = value.split(ws).toSeq |
| |
| var warningsList: List[WarnID] = Nil |
| warnIDs.foreach { warnIDString => |
| { |
| WarnID.find(warnIDString) match { |
| case None => log(LogLevel.Warning, "Ignoring unknown warning identifier: %s", warnIDString) |
| case Some(foundID) => warningsList = foundID :: warningsList |
| } |
| } |
| } |
| this.copy(suppressSchemaDefinitionWarnings = warningsList) |
| } |
| case "allowexternalpathexpressions" => this.copy(allowExternalPathExpressions = java.lang.Boolean.valueOf(value)) |
| case "erroronunsupportedjavaversion" => this.copy(errorOnUnsupportedJavaVersion = java.lang.Boolean.valueOf(value)) |
| case _ => { |
| log(LogLevel.Warning, "Ignoring unknown tunable: %s", tunable) |
| this |
| } |
| } |
| } |
| |
| def notSuppressedWarning(warnID: WarnID) = |
| !suppressSchemaDefinitionWarnings.contains(warnID) && |
| !suppressSchemaDefinitionWarnings.contains(WarnID.All) |
| |
| def maximumSimpleElementSizeInBytes: Long = this.maxFieldContentLengthInBytes |
| def maximumSimpleElementSizeInCharacters: Long = this.maxFieldContentLengthInBytes |
| def maximumForwardSpeculationLengthInBytes: Long = this.maxFieldContentLengthInBytes |
| def maximumRegexMatchLengthInCharacters: Long = this.maxFieldContentLengthInBytes |
| def defaultInitialRegexMatchLimitInChars: Long = this.defaultInitRegexMatchLimitInChars |
| |
| } |