diff --git a/README.md b/README.md index 0c49b0c80..d6ebc04a2 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,11 @@ Open Source Physics (OSP) https://github.com/OpenSourcePhysics/osp/tree/swingJS TrackerJS https://physlets.org/tracker/trackerJS +InChI-SwingJS https://github.com/BobHanson/InChI-SwingJS + +OpenChemLib (OCL-SwingJS) https://github.com/BobHanson/OCL-SwingJS + + # QuickStart diff --git a/sources/net.sf.j2s.core/dist/swingjs/SwingJS-site.zip b/sources/net.sf.j2s.core/dist/swingjs/SwingJS-site.zip index e738f20ae..ad5c5e7ff 100644 Binary files a/sources/net.sf.j2s.core/dist/swingjs/SwingJS-site.zip and b/sources/net.sf.j2s.core/dist/swingjs/SwingJS-site.zip differ diff --git a/sources/net.sf.j2s.core/dist/swingjs/j2s.core.jar b/sources/net.sf.j2s.core/dist/swingjs/j2s.core.jar index 805c52281..2af8cf86f 100644 Binary files a/sources/net.sf.j2s.core/dist/swingjs/j2s.core.jar and b/sources/net.sf.j2s.core/dist/swingjs/j2s.core.jar differ diff --git a/sources/net.sf.j2s.core/dist/swingjs/timestamp b/sources/net.sf.j2s.core/dist/swingjs/timestamp index d6484515e..22759d26c 100644 --- a/sources/net.sf.j2s.core/dist/swingjs/timestamp +++ b/sources/net.sf.j2s.core/dist/swingjs/timestamp @@ -1 +1 @@ -20241217101652 +20250419180504 diff --git a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/SwingJS-site.zip b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/SwingJS-site.zip index e738f20ae..ad5c5e7ff 100644 Binary files a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/SwingJS-site.zip and b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/SwingJS-site.zip differ diff --git a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/j2s.core.jar b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/j2s.core.jar index 805c52281..2af8cf86f 100644 Binary files a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/j2s.core.jar and b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/j2s.core.jar differ diff --git a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/timestamp b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/timestamp index d6484515e..22759d26c 100644 --- a/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/timestamp +++ b/sources/net.sf.j2s.core/dist/swingjs/ver/5.0.1/timestamp @@ -1 +1 @@ -20241217101652 +20250419180504 diff --git a/sources/net.sf.j2s.core/src/j2s/CorePlugin.java b/sources/net.sf.j2s.core/src/j2s/CorePlugin.java index c78c1cd61..57cc547e2 100644 --- a/sources/net.sf.j2s.core/src/j2s/CorePlugin.java +++ b/sources/net.sf.j2s.core/src/j2s/CorePlugin.java @@ -26,11 +26,15 @@ public class CorePlugin extends Plugin { * the actual "x.y.z" version is specified in plugin.xml. * */ - public static String VERSION = "5.0.1-v4"; + public static String VERSION = "5.0.1-v7"; // if you change the x.x.x number, be sure to also indicate that in // j2sApplet.js and also (Bob only) update.bat, update-clean.bat + // BH 2025.03.21 -- 5.0.1-v7 adds @j2sAlias with no name as "strip $"; multiple signatures distinguished with multiple $ appended, starting with "$$" + // BH 2025.03.21 -- 5.0.1-v7 adds @j2sExport equivalent to @j2sAlias for all public methods + // BH 2025.03.05 -- 5.0.1-v6 adds native interface methods for WASM + // BH 2025.02.22 -- 5.0.1-v5 fixes Iterable AtomIterator::new missing [this,null] in generator // BH 2024.07.14 -- 5.0.1-v4 fixes numerical array initializer using characters ['a','b',...] // BH 2024.02.22 -- 5.0.1-v3 fixes long extension issue causing MutableBitInteger to miscalculate subtraction(no change in version #) // BH 2023.11.27 -- 5.0.1-v2 final refactoring and creatiton of J2SUtil diff --git a/sources/net.sf.j2s.core/src/j2s/swingjs/Java2ScriptVisitor.java b/sources/net.sf.j2s.core/src/j2s/swingjs/Java2ScriptVisitor.java index 4f9119115..4efde18e2 100644 --- a/sources/net.sf.j2s.core/src/j2s/swingjs/Java2ScriptVisitor.java +++ b/sources/net.sf.j2s.core/src/j2s/swingjs/Java2ScriptVisitor.java @@ -31,6 +31,10 @@ // TODO: superclass inheritance for JAXB XmlAccessorType // TODO: Transpiler bug allows static String name, but JavaScript function().name is read-only and will be "clazz" +//BH 2025.03.21 -- 5.0.1-v7 adds @j2sAlias with no name as "strip $"; multiple signatures distinguished with multiple $ appended, starting with "$$" +//BH 2025.03.21 -- 5.0.1-v7 adds @j2sExport equivalent to @j2sAlias for all public methods +//BH 2025.03.05 -- 5.0.1-v6 adds native interface methods for WASM +//BH 2025.02.22 -- 5.0.1-v5 fixes Iterable AtomIterator::new missing [this,null] in generator //BH 2024.07.14 -- 5.0.1-v4 fixes numerical array initializer using characters ['a','b',...], but not new int[] { "test".charAt(3) } //BH 2024.02.22 -- 5.0.1-v3 fixes long extension issue causing MutableBitInteger to miscalculate subtraction(no change in version #) //BH 2023.11.27 -- 5.0.1-v2 final refactoring and creatiton of J2SUtil @@ -407,7 +411,7 @@ private Java2ScriptVisitor setInnerGlobals(Java2ScriptVisitor parent, ASTNode no package_htFinalVarToJ2sName = parent.package_htFinalVarToJ2sName; package_htClassKeyToVisitedFinalVars = parent.package_htClassKeyToVisitedFinalVars; - + package_outerFinalKey = parent.package_outerFinalKey; // flag for wrapping lambda Class::method syntax with $$ function @@ -602,9 +606,9 @@ private void addApplication() { private boolean checkAddApplet(ITypeBinding binding) { if (Modifier.isAbstract(binding.getModifiers())) return false; - //IType bound = (IType) binding.getJavaElement(); + // IType bound = (IType) binding.getJavaElement(); // How to compare this with JApplet? - //ITypeBinding b = binding; + // ITypeBinding b = binding; while ((binding = binding.getSuperclass()) != null) { String name = binding.getQualifiedName(); if ("javax.swing.JApplet".equals(name) || "java.applet.Applet".equals(name)) { @@ -691,7 +695,7 @@ public boolean visit(Block node) { buffer.append("{\n"); ASTNode parent = node.getParent(); if (parent instanceof TypeDeclaration) { - //Javadoc javadoc = ((TypeDeclaration) parent).getJavadoc(); + // Javadoc javadoc = ((TypeDeclaration) parent).getJavadoc(); } else if (parent instanceof MethodDeclaration && !((MethodDeclaration) parent).isConstructor() || parent instanceof Initializer) { @@ -796,11 +800,13 @@ private void addConstructor(ITypeBinding javaClass, // javaClass cannot be a lambda expression, because this is Xxxx::new String key = javaClass.getKey(); String finals = listFinalVariables(package_htClassKeyToVisitedFinalVars.get(key), package_outerFinalKey); - if (finals != null) { - buffer.append("this,").append(finals); - if (params.length() > 0) - buffer.append(","); - } + // for Lambda_C we need [this,null], because maybe there are finals expressed + // elsewhere + // if (finals != null) { + buffer.append("this,").append(finals); + if (params.length() > 0) + buffer.append(","); + // } buffer.append(params).append("]"); } else if (!isDefault) { IMethodBinding constructorMethodDeclaration = (constructorMethodBinding == null ? null @@ -811,7 +817,6 @@ private void addConstructor(ITypeBinding javaClass, buffer.append(")"); } - /** * 3.2.9.v1c * @@ -963,11 +968,10 @@ private String processLocalInstance(ASTNode node, ASTNode anonymousClassDeclarat IMethodBinding constructorDeclaration; String anonymousSuperclassName; String anonName; - - + if (lambdaType == NOT_LAMBDA) { // anonymous class - + IMethodBinding constructorBinding = ((ClassInstanceCreation) node).resolveConstructorBinding(); anonymousSuperclassName = getUnreplacedJavaClassNameSuperNoBrackets(binding); if (anonymousSuperclassName != null) @@ -1031,23 +1035,23 @@ private boolean addInnerDeclaration(ASTNode node, ITypeBinding binding, int lamb boolean wasAnonymous = class_isAnonymousOrLocal; String key = (anonName == null ? binding.getKey() : anonName); class_isAnonymousOrLocal = true; - + Set lastVisitedVars = class_visitedFinalVars; Set myVisitedVars = class_visitedFinalVars = new HashSet<>(); this.package_currentFinalKey = key; package_htClassKeyToVisitedFinalVars.put(key, myVisitedVars); if (lambdaType != NOT_LAMBDA) { - isStatic = addClassOrInterface(node, binding, null, 'm'); + isStatic = addClassOrInterface(node, binding, null, 'm', false); if (lambdaType == LAMBDA_METHOD) buffer.append("); return "); else buffer.append(", "); } else if (isTrulyLocal) { - // This is the second pass (class already defined) for a class - // defined within a method. + // This is the second pass (class already defined) for a class + // defined within a method. this.package_currentFinalKey = key = class_fullName; package_htClassKeyToVisitedFinalVars.put(class_fullName, myVisitedVars); - addClassOrInterface(node, binding, bodyDeclarations, 'l'); + addClassOrInterface(node, binding, bodyDeclarations, 'l', false); } else { buffer.append("("); ITypeBinding superclass = binding.getSuperclass(); @@ -1060,7 +1064,7 @@ private boolean addInnerDeclaration(ASTNode node, ITypeBinding binding, int lamb if (superfinals != null) myVisitedVars.addAll(superfinals); } - addClassOrInterface(node, binding, ((AnonymousClassDeclaration) node).bodyDeclarations(), 'a'); + addClassOrInterface(node, binding, ((AnonymousClassDeclaration) node).bodyDeclarations(), 'a', false); buffer.append(", "); } class_visitedFinalVars = lastVisitedVars; @@ -1222,7 +1226,7 @@ public boolean visit(EnumConstantDeclaration node) { public boolean visit(EnumDeclaration node) { setPackage(null); - addClassOrInterface(node, node.resolveBinding(), node.bodyDeclarations(), 'e'); + addClassOrInterface(node, node.resolveBinding(), node.bodyDeclarations(), 'e', false); return false; } @@ -1311,7 +1315,7 @@ public boolean visit(MethodDeclaration node) { /** * Called by visit(MethodDeclaration) as well as addLambdaMethod(). * - * @param mnode TODO + * @param mnode will be null for a lambda method or expression * @param mBinding * @param parameters * @param body @@ -1319,11 +1323,13 @@ public boolean visit(MethodDeclaration node) { * @param abstractMethodList to fill if not constructor, static, private, * abstract or lambda method or parameterless * @param lambdaType + * @param exportNames */ @SuppressWarnings("null") private void processMethodDeclaration(MethodDeclaration mnode, IMethodBinding mBinding, List parameters, - ASTNode body, boolean isConstructor, List abstractMethodList, int lambdaType) { - String alias = (mnode == null ? null : checkJ2SMethodDoc(mnode)); + ASTNode body, boolean isConstructor, List abstractMethodList, int lambdaType, + HashMap exportNames, boolean exportAll) { + boolean isAsync = (mnode != null && NativeDoc.hasJ2STag(mnode.getJavadoc(), "@j2sAsync")); int mods = mBinding.getModifiers(); @@ -1350,8 +1356,17 @@ private void processMethodDeclaration(MethodDeclaration mnode, IMethodBinding mB // allows us to catalog method names for an interface or abstract class return; } - String quotedFinalNameOrArray = getMethodNameWithSyntheticBridgeForDeclaration(mBinding, isConstructor, alias, - qualification, lambdaType); + // An alias is an added name that serves as another option + // for the method. For example, just "suppressHydrogens" rather than + // "suppressHydrogens$org_openscience_cdk_interfaces_IAtomContainer" + // This is particularly useful for classes that are expected to be called + // directly by JavaScript. + String alias = (mnode == null ? null : getJ2SAliasName(mnode)); + if (exportAll && isPublic && mnode != null && alias == null) { + alias = ""; + } + String quotedFinalNameOrArray = getMethodNameWithSyntheticBridgeForDeclaration(mBinding, isConstructor, + qualification, lambdaType, alias, exportNames, exportAll); boolean isMain = (isStatic && isPublic && mBinding.getName().equals("main") && mBinding.getKey().indexOf(";.main([Ljava/lang/String;)V") >= 0); if (isMain) { @@ -1380,7 +1395,7 @@ private void processMethodDeclaration(MethodDeclaration mnode, IMethodBinding mB buffer.append(") "); if (lambdaType != NOT_LAMBDA) { - addLambdaBody(body); + addLambdaBody(body, mBinding.getReturnType()); if (body == null) return; } else if (isConstructor) { @@ -1413,10 +1428,15 @@ private void processMethodDeclaration(MethodDeclaration mnode, IMethodBinding mB body.accept(this); } meth_current = prevMethod; - if (isStatic || isConstructor) + if (isStatic || isConstructor) { buffer.append(", ").append(isNative ? 2 : 1); - else if (isPrivate) + if (isNative) { + ITypeBinding ret = mBinding.getReturnType(); + buffer.append(", \"" + getUnreplacedJavaClassNameQualified(ret) + "\""); + } + } else if (isPrivate) { buffer.append(", " + getPrivateVar(mClass, false)); + } buffer.append(");\n"); } @@ -1480,17 +1500,13 @@ private boolean addMethodInvocation(SimpleName javaQualifier, List arguments, boolean isPrivateAndNotStatic = isPrivate && !isStatic; String privateVar = (isPrivateAndNotStatic ? getPrivateVar(declaringClass, false) : null); boolean doLogMethodCalled = (!isPrivate && global_htMethodsCalled != null); - boolean needBname = ( - !isStatic - && (lambdaArity < 0 + boolean needBname = (!isStatic && (lambdaArity < 0 && (expression == null - ? !areEqual(declaringClass, class_typeBinding) - && !class_typeBinding.isAssignmentCompatible(declaringClass) - : expression instanceof ThisExpression - && ((ThisExpression) expression).getQualifier() != null) - || class_localType == LAMBDA_EXPRESSION) - ); - + ? !areEqual(declaringClass, class_typeBinding) + && !class_typeBinding.isAssignmentCompatible(declaringClass) + : expression instanceof ThisExpression && ((ThisExpression) expression).getQualifier() != null) + || class_localType == LAMBDA_EXPRESSION)); + String bname = (needBname ? getThisRefOrSyntheticReference(javaQualifier, declaringClass, null) : null); // add the qualifier int pt = buffer.length(); @@ -1918,10 +1934,11 @@ public boolean visit(TypeDeclaration node) { setPackage(null); // anonymous will never come through here. It will be routed directly to // addClassOrInterface + boolean doExport = (NativeDoc.hasJ2STag(node.getJavadoc(), "@j2sExport")); List declarations = node.bodyDeclarations(); ITypeBinding binding = node.resolveBinding(); addClassOrInterface(node, binding, declarations, - node.isInterface() ? 'i' : node.isLocalTypeDeclaration() ? 'l' : 'c'); + node.isInterface() ? 'i' : node.isLocalTypeDeclaration() ? 'l' : 'c', doExport); return false; } @@ -1991,15 +2008,17 @@ private void appendClinit() { * * @param node * @param binding - * @param BodyDeclarations * @param type 'a' (anonymous class), 'e' (Enum), 'i' (Interface), * 'l' (local), 'm' (LambdaExpression), '@' * (_at_interface AnnotationType), or 'c' (standard * class) + * @param exportAll if true, all public methods will get unqualified names followed by enough "$" to make them unique. + * @param BodyDeclarations * @return localName */ @SuppressWarnings({ "null", "unchecked" }) - private boolean addClassOrInterface(ASTNode node, ITypeBinding binding, List bodyDeclarations, char type) { + private boolean addClassOrInterface(ASTNode node, ITypeBinding binding, List bodyDeclarations, char type, + boolean exportAll) { if (binding == null) return false; ASTNode parent = node.getParent(); @@ -2050,7 +2069,7 @@ private boolean addClassOrInterface(ASTNode node, ITypeBinding binding, List if (isTrulyLocal) { tempVisitor.addInnerDeclaration(node, binding, NOT_LAMBDA, true, bodyDeclarations, null); } else { - tempVisitor.addClassOrInterface(node, binding, bodyDeclarations, type); + tempVisitor.addClassOrInterface(node, binding, bodyDeclarations, type, false); } // append it to our TrailingBuffer @@ -2367,7 +2386,6 @@ private boolean addClassOrInterface(ASTNode node, ITypeBinding binding, List List enums = (isEnum ? new ArrayList<>() : null); List fields = (haveFieldMethodAnnotations || isInterface || isLambda || isEnum ? null : new ArrayList<>()); - List methods = (haveFieldMethodAnnotations || isAnnotation || fields != null ? new ArrayList<>() : null); @@ -2477,6 +2495,7 @@ && checkAnnotations(element, CHECK_J2S_IGNORE_AND_ANNOTATIONS)) { buffer.append("C$.prototype.annotationType = function() { return this.getClass$() };\n"); trailingBuffer.append("C$.$getMembers$ = function() {" + varOrLet + "a=[];\n"); } + HashMap exportNames = new HashMap<>(); for (Iterator iter = bodyDeclarations.iterator(); iter.hasNext();) { ASTNode element = (ASTNode) iter.next(); if (element instanceof MethodDeclaration) { @@ -2496,7 +2515,7 @@ && checkAnnotations(element, CHECK_J2S_IGNORE_AND_ANNOTATIONS)) { class_defpt = buffer.length(); } processMethodDeclaration(mnode, method, mnode.parameters(), mnode.getBody(), mnode.isConstructor(), - abstractMethodList, NOT_LAMBDA); + abstractMethodList, NOT_LAMBDA, exportNames, exportAll); if (class_defpt >= 0) { defaults.append(buffer.substring(class_defpt)); buffer.setLength(class_defpt); @@ -2520,7 +2539,7 @@ && checkAnnotations(element, CHECK_J2S_IGNORE_AND_ANNOTATIONS)) { addSyntheticBridges(binding, abstractMethodList, defaults, true); if (defaults.length() > 0) { buffer.append(defaults.indexOf("C$$") >= 0 ? "var C$$ = C$;" : "") - .append("C$.$defaults$ = function(C$){\n").append(defaults).append("};"); + .append("C$.$defaults$ = function(C$){\n").append(defaults).append("};"); } } else { addSyntheticBridges(binding, abstractMethodList, buffer, false); @@ -2647,7 +2666,7 @@ private void addEnumConstants(EnumDeclaration e, List e if (anonDeclare != null) { ITypeBinding dbinding = anonDeclare.resolveBinding(); // BH: add the anonymous class definition inline! - addClassOrInterface(anonDeclare, dbinding, anonDeclare.bodyDeclarations(), 'a'); + addClassOrInterface(anonDeclare, dbinding, anonDeclare.bodyDeclarations(), 'a', false); anonName = getUnreplacedJavaClassNameQualified(dbinding); buffer.append("\n"); } @@ -2745,7 +2764,6 @@ private boolean addInnerTypeInstance(ASTNode node, ITypeBinding binding, ITypeBi } // add final variable array - String key = (anonName == null ? binding.getKey() : anonName); Set list = package_htClassKeyToVisitedFinalVars.get(key); String finals = listFinalVariables(list, package_outerFinalKey); @@ -3057,15 +3075,12 @@ private void fixCharArrayInit(StringBuffer buffer, List list, int toCha if (toChar != 0) { String s = buffer.substring(pt); // only fix "x" and number - // not fixed: int[] a = new int[] {"test".charAt(0)} + // not fixed: int[] a = new int[] {"test".charAt(0)} try { - if (pt > 0 && (s.charAt(0) == '"') != (toChar == 1)) { - buffer.replace(pt, buffer.length(), - (toChar == 1 ? - "\"" + ((char) Integer.parseInt(s)) + "\"" - : s.length() == 3 ? "" + (byte) s.charAt(1) : s) - ); - } + if (pt > 0 && (s.charAt(0) == '"') != (toChar == 1)) { + buffer.replace(pt, buffer.length(), (toChar == 1 ? "\"" + ((char) Integer.parseInt(s)) + "\"" + : s.length() == 3 ? "" + (byte) s.charAt(1) : s)); + } } catch (@SuppressWarnings("unused") Exception e) { // ignore } @@ -3545,7 +3560,7 @@ public boolean visit(InfixExpression node) { String op = node.getOperator().toString(); - //boolean isEqualType = (op.equals("==") || op.equals("!=")); + // boolean isEqualType = (op.equals("==") || op.equals("!=")); boolean isBitwise = isBitwiseBinaryOperator(node); boolean isComparison = (!isBitwise && "!==<=>=".indexOf(op) >= 0); ITypeBinding leftTypeBinding = left.resolveTypeBinding(); @@ -4613,7 +4628,7 @@ private boolean addPrimitiveTypedExpression(Expression left, IVariableBinding as String prefix = (isAssignment ? "=" : ""); boolean fromChar = "char".equals(rightName); boolean fromFloat = ("float double".indexOf(rightName) >= 0); - //boolean toFloat = ("float double".indexOf(leftName) >= 0); + // boolean toFloat = ("float double".indexOf(leftName) >= 0); boolean fromIntType = ("long int short byte".indexOf(rightName) >= 0); boolean toLong = "long".equals(leftName); boolean fromLong = "long".equals(rightName); @@ -5001,7 +5016,7 @@ private void appendBoxFunction(Expression exp, ITypeBinding typeBinding, String temp_iref = null; String s1 = null; - //int pt1 = buffer.length(); + // int pt1 = buffer.length(); if (pt >= 0 && iref == null && (exp instanceof FieldAccess) && !haveDirectAccess(exp) && !isStatic(typeBinding)) { iref = pushFieldQualifier(exp); @@ -5675,17 +5690,19 @@ private static ASTNode getAbstractOrAnonymousParentForNode(ASTNode node) { * declaration Clazz.newMeth(...). Bracketed returns tell Clazz to create * multiple aliases for the same method. * - * @param node * @param mBinding * @param isConstructor - * @param alias name provided using at_j2sAlias * @param mode - * @param abstractMethodList * @param lambdaType + * @param alias name provided using at_j2sAlias + * @param exportNames TODO + * @param node + * @param abstractMethodList + * * @return j2s-qualified name or an array of j2s-qualified names */ - String getMethodNameWithSyntheticBridgeForDeclaration(IMethodBinding mBinding, boolean isConstructor, String alias, - int mode, int lambdaType) { + String getMethodNameWithSyntheticBridgeForDeclaration(IMethodBinding mBinding, boolean isConstructor, int mode, + int lambdaType, String alias, HashMap exportNames, boolean exportAll) { List names = new ArrayList(); String nodeName = mBinding.getName(); String methodName = (isConstructor ? "c$" : nodeName); @@ -5698,8 +5715,27 @@ String getMethodNameWithSyntheticBridgeForDeclaration(IMethodBinding mBinding, b // $O$O$O for lambda as well names.add(getFinalMethodNameWith$Params(methodName, mBinding, null, false, mtype)); } - if (alias != null) - names.add(alias); + if (alias != null) { + if (alias.length() == 0) { + // just "@j2sAlias" with no name given, or public and @j2sExport for the class + int pt = methodName.indexOf("$"); + if (pt == 0) { + // just concerned this is possible. Maybe not? + alias = null; + } else { + alias = exportNames.get(methodName); + if (alias == null) { + alias = (pt < 0 ? methodName : methodName.substring(0, pt)); + } else { + // skipping xxxx$ because that could be already xxxx$(); + alias += (alias.endsWith("$") ? "$" : "$$"); + } + exportNames.put(methodName, alias); + } + } + if (alias != null) + names.add(alias); + } if ((mode & METHOD_ADD_GENERIC) != 0) { // interesting case of this in Test_ClassBase, where different interfaces and // the superclass @@ -6217,7 +6253,7 @@ private boolean addJ2SDoc(ASTNode node) { * @param node * @return alias */ - private String checkJ2SMethodDoc(MethodDeclaration node) { + private String getJ2SAliasName(ASTNode node) { List j2sJavadoc; if (package_mapBlockJavadoc == null || (j2sJavadoc = getJ2sJavadoc(node, DOC_CHECK_ONLY)) == null) return null; @@ -6228,7 +6264,7 @@ private String checkJ2SMethodDoc(MethodDeclaration node) { if (tags == null || tags.size() == 0 || (tag = NativeDoc.getTag(tags, "@j2sAlias")) == null) continue; List fragments = tag.fragments(); - return (fragments == null || fragments.size() == 0 ? null : fragments.get(0).toString().trim()); + return (fragments == null ? null : fragments.size() == 0 ? "" : fragments.get(0).toString().trim()); } return null; } @@ -6270,9 +6306,9 @@ private boolean checkAnnotations(BodyDeclaration node, int mode) { if (obj instanceof Annotation) { if (!addAnnotation((Annotation) obj, node, mode)) return false; - } } } + } return true; } @@ -6451,7 +6487,7 @@ private void addDummyClassForPackageOnlyFile() { } public boolean visit(AnnotationTypeDeclaration node) { - addClassOrInterface(node, node.resolveBinding(), node.bodyDeclarations(), '@'); + addClassOrInterface(node, node.resolveBinding(), node.bodyDeclarations(), '@', false); return false; } @@ -6780,6 +6816,13 @@ static String checkClassReplacement(String className) { private static String[] nonQualifiedPackages; /** + * Be causious with this flag. Use it only for JavaScript-only classes that will + * only be accessed by JavaScript, never Java. + * + * For Java classes, use .at.j2sAlias on the class in order to indicate that all + * the public methods in the class should be aliased without their $ signature + * qualifiers. + * * .j2s option j2s.compiler.nonqualified.packages/classes * * @param names semicolon-separated list. For example, @@ -7069,12 +7112,12 @@ public static void addClassAnnotations(Java2ScriptVisitor visitor, int accessTyp if (methods.contains(mBinding)) methods.remove(mBinding); if (accessType != NOT_JAXB) - mBinding =getJAXBGetMethod(accessType, mBinding, methods, false); + mBinding = getJAXBGetMethod(accessType, mBinding, methods, false); if (mBinding == null) continue; varName = "M:" + mBinding.getName(); methodSignature = visitor.getMethodNameWithSyntheticBridgeForDeclaration(mBinding, - mBinding.isConstructor(), null, METHOD_FULLY_QUALIFIED, NOT_LAMBDA); + mBinding.isConstructor(), METHOD_FULLY_QUALIFIED, NOT_LAMBDA, null, null, false); type = mBinding.getReturnType(); } else if (a.node instanceof AnnotationTypeMemberDeclaration) { MethodDeclaration method = (MethodDeclaration) a.node; @@ -7567,8 +7610,9 @@ private void addLambdaReuse(int pt, String anonName) { buffer.append("(" + anonName + "$||(" + anonName + "$=(").append(tmp).append(")))"); } - private void addLambdaBody(ASTNode body) { + private void addLambdaBody(ASTNode body, ITypeBinding retType) { if (body instanceof Block) { + buffer.append("/*block*/"); body.accept(this); } else { // there may be no return, but we still want to do this @@ -7576,7 +7620,7 @@ private void addLambdaBody(ASTNode body) { if (body == null) return; // handled elsewhere buffer.append("("); - body.accept(this); + addExpressionAsTargetType((Expression) body, retType, "r", null); buffer.append(");}"); } } @@ -7604,13 +7648,14 @@ private boolean addLambdaClass(ASTNode lnode, IMethodBinding mBinding) { List params = node.parameters(); int localType = class_localType; class_localType = LAMBDA_EXPRESSION; - processMethodDeclaration(null, mBinding, params, node.getBody(), false, null, LAMBDA_EXPRESSION); + processMethodDeclaration(null, mBinding, params, node.getBody(), false, null, LAMBDA_EXPRESSION, null, + false); class_localType = localType; return true; } if (lnode instanceof CreationReference) { buffer.append("/*lambda_C*/"); - processMethodDeclaration(null, mBinding, null, null, false, null, LAMBDA_CREATION); + processMethodDeclaration(null, mBinding, null, null, false, null, LAMBDA_CREATION, null, false); CreationReference node = (CreationReference) lnode; Type ctype = node.getType(); ITypeBinding binding = ctype.resolveBinding(); @@ -7652,7 +7697,7 @@ private boolean addLambdaClass(ASTNode lnode, IMethodBinding mBinding) { log("??? addLambdaMethod " + lnode.getClass().getName()); return false; } - processMethodDeclaration(null, mBinding, null, null, false, null, LAMBDA_METHOD); + processMethodDeclaration(null, mBinding, null, null, false, null, LAMBDA_METHOD, null, false); boolean isStatic = addMethodInvocation(identifier, null, mBinding1, exp, mBinding.getParameterTypes().length); buffer.append("});\n"); return isStatic; diff --git a/sources/net.sf.j2s.java.core/dist/SwingJS-site.zip b/sources/net.sf.j2s.java.core/dist/SwingJS-site.zip index 573368c76..9d7073079 100644 Binary files a/sources/net.sf.j2s.java.core/dist/SwingJS-site.zip and b/sources/net.sf.j2s.java.core/dist/SwingJS-site.zip differ diff --git a/sources/net.sf.j2s.java.core/dist_to_jsmol/Jmol-j2s-site.zip b/sources/net.sf.j2s.java.core/dist_to_jsmol/Jmol-j2s-site.zip index d73aafd28..a42538ba9 100644 Binary files a/sources/net.sf.j2s.java.core/dist_to_jsmol/Jmol-j2s-site.zip and b/sources/net.sf.j2s.java.core/dist_to_jsmol/Jmol-j2s-site.zip differ diff --git a/sources/net.sf.j2s.java.core/resources/_ES6/README.TXT b/sources/net.sf.j2s.java.core/resources/_ES6/README.TXT index 5f14a2e09..ef0d483ec 100644 --- a/sources/net.sf.j2s.java.core/resources/_ES6/README.TXT +++ b/sources/net.sf.j2s.java.core/resources/_ES6/README.TXT @@ -1,3 +1,3 @@ -The /_ES6 directory can contain JavaScript ES6 module code that is not to be compressed using GCC. +The /_ES6 directory can contain JavaScript ES6 module code and WASM that is not to be compressed using GCC. -For example, molfile_to_inchi.wasm and wasi.esm.js \ No newline at end of file +For example, jnainchi.wasm and jnainchi.js from Emscripten \ No newline at end of file diff --git a/sources/net.sf.j2s.java.core/resources/_WASM/README.TXT b/sources/net.sf.j2s.java.core/resources/_WASM/README.TXT deleted file mode 100644 index 780de7bce..000000000 --- a/sources/net.sf.j2s.java.core/resources/_WASM/README.TXT +++ /dev/null @@ -1 +0,0 @@ -The /_WASM directory can contain Web Assembly code. \ No newline at end of file diff --git a/sources/net.sf.j2s.java.core/src/com/sun/jna/Library.java b/sources/net.sf.j2s.java.core/src/com/sun/jna/Library.java new file mode 100644 index 000000000..75bf50c53 --- /dev/null +++ b/sources/net.sf.j2s.java.core/src/com/sun/jna/Library.java @@ -0,0 +1,274 @@ +/* + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna; + +//import com.sun.jna.internal.ReflectionUtils; +//import java.lang.reflect.InvocationHandler; +//import java.lang.reflect.Method; +//import java.lang.reflect.Proxy; +//import java.util.HashMap; +//import java.util.Map; +//import java.util.WeakHashMap; + +/** Derive from this interface for all native library definitions. + * + * Define an instance of your library like this: + *

+ * MyNativeLibrary INSTANCE = (MyNativeLibrary)
+ *     Native.load("mylib", MyNativeLibrary.class);
+ * 
+ *

+ * By convention, method names are identical to the native names, although you + * can map java names to different native names by providing a + * {@link FunctionMapper} as a value for key {@link #OPTION_FUNCTION_MAPPER} + * in the options map passed to the + * {@link Native#load(String, Class, Map)} call. + *

+ * Although the names for structures and structure fields may be chosen + * arbitrarily, they should correspond as closely as possible to the native + * definitions. The same is true for parameter names. + *

+ * This interface supports multiple, concurrent invocations of any library + * methods on the Java side. Check your library documentation for its + * multi-threading requirements on the native side. If a library is not safe + * for simultaneous multi-threaded access, consider using + * {@link Native#synchronizedLibrary} to prevent simultaneous multi-threaded + * access to the native code. + *

+ * Optional fields
+ * Interface options will be automatically propagated to structures defined + * within the library provided a call to + * {@link Native#load(String,Class,Map)} is made prior to instantiating + * any of those structures. One common way of ensuring this is to declare + * an INSTANCE field in the interface which holds the + * load result. + *

+ * OPTIONS (an instance of {@link Map}), + * TYPE_MAPPER (an instance of {@link TypeMapper}), + * STRUCTURE_ALIGNMENT (one of the alignment types defined in + * {@link Structure}), and STRING_ENCODING (a {@link String}) may also + * be defined. If no instance of the interface has been instantiated, these + * fields will be used to determine customization settings for structures and + * methods defined within the interface. + *

+ * + * @author Todd Fast, todd.fast@sun.com + * @author Timothy Wall, twalljava@dev.java.net + */ +public interface Library { +// /** Option key for a {@link TypeMapper} for the library. */ +// String OPTION_TYPE_MAPPER = "type-mapper"; +// /** Option key for a {@link FunctionMapper} for the library. */ +// String OPTION_FUNCTION_MAPPER = "function-mapper"; +// /** Option key for an {@link InvocationMapper} for the library. */ +// String OPTION_INVOCATION_MAPPER = "invocation-mapper"; +// /** Option key for structure alignment type ({@link Integer}), which should +// * be one of the predefined alignment types in {@link Structure}. +// */ +// String OPTION_STRUCTURE_ALIGNMENT = "structure-alignment"; +// /**

Option key for per-library String encoding. This affects conversions +// * between Java unicode and native (const char*) strings (as +// * arguments or Structure fields). +// *

+// * Defaults to {@link Native#getDefaultStringEncoding()}. +// */ +// String OPTION_STRING_ENCODING = "string-encoding"; +// /** Option key for a boolean flag to allow any Java class instance as a +// parameter. If no type mapper is found, the object is passed as a +// pointer. +// NOTE: This is for use with raw JNI interactions via the +// JNIEnv data structure. +// */ +// String OPTION_ALLOW_OBJECTS = "allow-objects"; +// /** Calling convention for the entire library. */ +// String OPTION_CALLING_CONVENTION = "calling-convention"; +// /** Flags to use when opening the native library (see {@link Native#open(String,int)}) */ +// String OPTION_OPEN_FLAGS = "open-flags"; +// /**

Class loader to use when searching for native libraries on the +// * resource path (classpath). If not provided the current thread's +// * context class loader is used.

+// * If extracted from the resource path (i.e. bundled in a jar file), the +// * loaded library's lifespan will mirror that of the class loader, which +// * means you can use the same library in isolated contexts without +// * conflict. +// */ +// String OPTION_CLASSLOADER = "classloader"; +// +// /** +// * Supports a custom symbol provider for the NativeLibrary (see {@link SymbolProvider}) +// */ +// String OPTION_SYMBOL_PROVIDER = "symbol-provider"; +// +// static class Handler implements InvocationHandler { +// +// static final Method OBJECT_TOSTRING; +// static final Method OBJECT_HASHCODE; +// static final Method OBJECT_EQUALS; +// +// static { +// try { +// OBJECT_TOSTRING = Object.class.getMethod("toString"); +// OBJECT_HASHCODE= Object.class.getMethod("hashCode"); +// OBJECT_EQUALS = Object.class.getMethod("equals", Object.class); +// } catch (Exception e) { +// throw new Error("Error retrieving Object.toString() method"); +// } +// } +// +// /** +// * FunctionInfo has to be immutable to to make the object visible +// * to other threads fully initialized. This is a prerequisite for +// * using the class in the double checked locking scenario of {@link Handler#invoke(Object, Method, Object[])} +// */ +// private static final class FunctionInfo { +// final InvocationHandler handler; +// final Function function; +// final boolean isVarArgs; +// final Object methodHandle; +// final Map options; +// final Class[] parameterTypes; +// +// FunctionInfo(Object mh) { +// this.handler = null; +// this.function = null; +// this.isVarArgs = false; +// this.options = null; +// this.parameterTypes = null; +// this.methodHandle = mh; +// } +// +// FunctionInfo(InvocationHandler handler, Function function, Class[] parameterTypes, boolean isVarArgs, Map options) { +// this.handler = handler; +// this.function = function; +// this.isVarArgs = isVarArgs; +// this.options = options; +// this.parameterTypes = parameterTypes; +// this.methodHandle = null; +// } +// } +// +// private final NativeLibrary nativeLibrary; +// private final Class interfaceClass; +// // Library invocation options +// private final Map options; +// private final InvocationMapper invocationMapper; +// private final Map functions = new WeakHashMap<>(); +// public Handler(String libname, Class interfaceClass, Map options) { +// +// if (libname != null && "".equals(libname.trim())) { +// throw new IllegalArgumentException("Invalid library name \"" + libname + "\""); +// } +// +// if (!interfaceClass.isInterface()) { +// throw new IllegalArgumentException(libname + " does not implement an interface: " + interfaceClass.getName()); +// } +// +// this.interfaceClass = interfaceClass; +// this.options = new HashMap<>(options); +// int callingConvention = AltCallingConvention.class.isAssignableFrom(interfaceClass) +// ? Function.ALT_CONVENTION +// : Function.C_CONVENTION; +// if (this.options.get(OPTION_CALLING_CONVENTION) == null) { +// this.options.put(OPTION_CALLING_CONVENTION, Integer.valueOf(callingConvention)); +// } +// if (this.options.get(OPTION_CLASSLOADER) == null) { +// this.options.put(OPTION_CLASSLOADER, interfaceClass.getClassLoader()); +// } +// this.nativeLibrary = NativeLibrary.getInstance(libname, this.options); +// invocationMapper = (InvocationMapper)this.options.get(OPTION_INVOCATION_MAPPER); +// } +// +// public NativeLibrary getNativeLibrary() { +// return nativeLibrary; +// } +// +// public String getLibraryName() { +// return nativeLibrary.getName(); +// } +// +// public Class getInterfaceClass() { +// return interfaceClass; +// } +// +// @Override +// public Object invoke(Object proxy, Method method, Object[] inArgs) +// throws Throwable { +// +// // Intercept Object methods +// if (OBJECT_TOSTRING.equals(method)) { +// return "Proxy interface to " + nativeLibrary; +// } else if (OBJECT_HASHCODE.equals(method)) { +// return Integer.valueOf(hashCode()); +// } else if (OBJECT_EQUALS.equals(method)) { +// Object o = inArgs[0]; +// if (o != null && Proxy.isProxyClass(o.getClass())) { +// return Function.valueOf(Proxy.getInvocationHandler(o) == this); +// } +// return Boolean.FALSE; +// } +// +// // Using the double-checked locking pattern to speed up function calls +// FunctionInfo f = functions.get(method); +// if(f == null) { +// synchronized(functions) { +// f = functions.get(method); +// if (f == null) { +// boolean isDefault = ReflectionUtils.isDefault(method); +// if(! isDefault) { +// boolean isVarArgs = Function.isVarArgs(method); +// InvocationHandler handler = null; +// if (invocationMapper != null) { +// handler = invocationMapper.getInvocationHandler(nativeLibrary, method); +// } +// Function function = null; +// Class[] parameterTypes = null; +// Map options = null; +// if (handler == null) { +// // Find the function to invoke +// function = nativeLibrary.getFunction(method.getName(), method); +// parameterTypes = method.getParameterTypes(); +// options = new HashMap<>(this.options); +// options.put(Function.OPTION_INVOKING_METHOD, method); +// } +// f = new FunctionInfo(handler, function, parameterTypes, isVarArgs, options); +// } else { +// f = new FunctionInfo(ReflectionUtils.getMethodHandle(method)); +// } +// functions.put(method, f); +// } +// } +// } +// if (f.methodHandle != null) { +// return ReflectionUtils.invokeDefaultMethod(proxy, f.methodHandle, inArgs); +// } else { +// if (f.isVarArgs) { +// inArgs = Function.concatenateVarArgs(inArgs); +// } +// if (f.handler != null) { +// return f.handler.invoke(proxy, method, inArgs); +// } +// return f.function.invoke(method, f.parameterTypes, method.getReturnType(), inArgs, f.options); +// } +// } +// } +} diff --git a/sources/net.sf.j2s.java.core/src/com/sun/jna/Native.java b/sources/net.sf.j2s.java.core/src/com/sun/jna/Native.java new file mode 100644 index 000000000..c313b9eb1 --- /dev/null +++ b/sources/net.sf.j2s.java.core/src/com/sun/jna/Native.java @@ -0,0 +1,2473 @@ +/* Copyright (c) 2007-2015 Timothy Wall, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna; + +//import java.awt.Component; +//import java.awt.GraphicsEnvironment; +//import java.awt.HeadlessException; +//import java.awt.Window; +//import java.io.File; +//import java.io.FileOutputStream; +//import java.io.FilenameFilter; +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.UnsupportedEncodingException; +//import java.lang.ref.Reference; +//import java.lang.ref.WeakReference; +//import java.lang.reflect.Array; +//import java.lang.reflect.Field; +//import java.lang.reflect.InvocationHandler; +//import java.lang.reflect.Method; +//import java.lang.reflect.Modifier; +//import java.lang.reflect.Proxy; +//import java.net.URI; +//import java.net.URISyntaxException; +//import java.net.URL; +//import java.net.URLClassLoader; +//import java.nio.Buffer; +//import java.nio.ByteBuffer; +//import java.nio.charset.Charset; +//import java.nio.charset.IllegalCharsetNameException; +//import java.nio.charset.UnsupportedCharsetException; +//import java.security.AccessController; +//import java.security.PrivilegedAction; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.Collections; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +//import java.util.StringTokenizer; +//import java.util.WeakHashMap; +// +//import com.sun.jna.Callback.UncaughtExceptionHandler; +//import com.sun.jna.Structure.FFIType; +//import java.util.logging.Level; +//import java.util.logging.Logger; + +/** Provides generation of invocation plumbing for a defined native + * library interface. Also provides various utilities for native operations. + *

+ * {@link #getTypeMapper} and {@link #getStructureAlignment} are provided + * to avoid having to explicitly pass these parameters to {@link Structure}s, + * which would require every {@link Structure} which requires custom mapping + * or alignment to define a constructor and pass parameters to the superclass. + * To avoid lots of boilerplate, the base {@link Structure} constructor + * figures out these properties based on its enclosing interface.

+ * + *

Library Loading

+ *

When JNA classes are loaded, the native shared library (jnidispatch) is + * loaded as well. An attempt is made to load it from the any paths defined + * in jna.boot.library.path (if defined), then the system library + * path using {@link System#loadLibrary}, unless jna.nosys=true. + * If not found, the appropriate library will be extracted from the class path + * (into a temporary directory if found within a jar file) and loaded from + * there, unless jna.noclasspath=true. If your system has + * additional security constraints regarding execution or load of files + * (SELinux, for example), you should probably install the native library in + * an accessible location and configure your system accordingly, rather than + * relying on JNA to extract the library from its own jar file.

+ *

To avoid the automatic unpacking (in situations where you want to force a + * failure if the JNA native library is not properly installed on the system), + * set the system property jna.nounpack=true. + *

+ *

While this class and its corresponding native library are loaded, the + * system property jna.loaded will be set. The property will be + * cleared when native support has been unloaded (i.e. the Native class and + * its underlying native support has been GC'd).

+ *

NOTE: all native functions are provided within this class to ensure that + * all other JNA-provided classes and objects are GC'd and/or + * finalized/disposed before this class is disposed and/or removed from + * memory (most notably Memory and any other class which by default frees its + * resources in a finalizer).

+ * + *

Native Library Loading

+ * Native libraries loaded via {@link #load(Class)} may be found in + * several locations. + * @see Library + * @author Todd Fast, todd.fast@sun.com + * @author twall@users.sf.net + */ +public final class Native {//implements Version { + +// private static final Logger LOG = Logger.getLogger(Native.class.getName()); +// +// public static final Charset DEFAULT_CHARSET; +// public static final String DEFAULT_ENCODING; +// static { +// // JNA used the defaultCharset to determine which encoding to use when +// // converting strings to native char*. The defaultCharset is set from +// // the system property file.encoding. Up to JDK 17 its value defaulted +// // to the system default encoding. From JDK 18 onwards its default value +// // changed to UTF-8. +// // JDK 18+ exposes the native encoding as the new system property +// // native.encoding, prior versions don't have that property and will +// // report NULL for it. +// // The algorithm is simple: If native.encoding is set, it will be used +// // else the original implementation of Charset#defaultCharset is used +// String nativeEncoding = System.getProperty("native.encoding"); +// Charset nativeCharset = null; +// if (nativeEncoding != null) { +// try { +// nativeCharset = Charset.forName(nativeEncoding); +// } catch (Exception ex) { +// LOG.log(Level.WARNING, "Failed to get charset for native.encoding value : '" + nativeEncoding + "'", ex); +// } +// } +// if (nativeCharset == null) { +// nativeCharset = Charset.defaultCharset(); +// } +// DEFAULT_CHARSET = nativeCharset; +// DEFAULT_ENCODING = nativeCharset.name(); +// } +// public static final boolean DEBUG_LOAD = Boolean.getBoolean("jna.debug_load"); +// public static final boolean DEBUG_JNA_LOAD = Boolean.getBoolean("jna.debug_load.jna"); +// private final static Level DEBUG_JNA_LOAD_LEVEL = DEBUG_JNA_LOAD ? Level.INFO : Level.FINE; +// +// // Used by tests, do not remove +// static String jnidispatchPath = null; +// private static final Map, Map> typeOptions = Collections.synchronizedMap(new WeakHashMap, Map>()); +// private static final Map, Reference> libraries = Collections.synchronizedMap(new WeakHashMap, Reference>()); +// private static final String _OPTION_ENCLOSING_LIBRARY = "enclosing-library"; +// private static final UncaughtExceptionHandler DEFAULT_HANDLER = +// new UncaughtExceptionHandler() { +// @Override +// public void uncaughtException(Callback c, Throwable e) { +// LOG.log(Level.WARNING, "JNA: Callback " + c + " threw the following exception", e); +// } +// }; +// private static UncaughtExceptionHandler callbackExceptionHandler = DEFAULT_HANDLER; +// +// /** The size of a native pointer (void*) on the current +// * platform, in bytes. +// */ +// public static final int POINTER_SIZE; +// /** Size of a native long type, in bytes. */ +// public static final int LONG_SIZE; +// /** Size of a native wchar_t type, in bytes. */ +// public static final int WCHAR_SIZE; +// /** Size of a native size_t type, in bytes. */ +// public static final int SIZE_T_SIZE; +// /** Size of a native bool type (C99 and later), in bytes. */ +// public static final int BOOL_SIZE; +// /** Size of a native long double type (C99 and later), in bytes. */ +// public static final int LONG_DOUBLE_SIZE; +// +// private static final int TYPE_VOIDP = 0; +// private static final int TYPE_LONG = 1; +// private static final int TYPE_WCHAR_T = 2; +// private static final int TYPE_SIZE_T = 3; +// private static final int TYPE_BOOL = 4; +// private static final int TYPE_LONG_DOUBLE = 5; +// +// static final int MAX_ALIGNMENT; +// static final int MAX_PADDING; +// +// /** +// * Version string must have the structure .. +// * a bugfix change in the native code increments revision, the minor is +// * incremented for backwards compatible changes and the major version +// * is changed for backwards incompatbile changes. +// * +// * @param expectedVersion +// * @param nativeVersion +// * @return true if nativeVersion describes a version compatible to expectedVersion +// */ +// static boolean isCompatibleVersion(String expectedVersion, String nativeVersion) { +// String[] expectedVersionParts = expectedVersion.split("\\."); +// String[] nativeVersionParts = nativeVersion.split("\\."); +// if(expectedVersionParts.length < 3 || nativeVersionParts.length < 3) { +// return false; +// } +// +// int expectedMajor = Integer.parseInt(expectedVersionParts[0]); +// int nativeMajor = Integer.parseInt(nativeVersionParts[0]); +// int expectedMinor = Integer.parseInt(expectedVersionParts[1]); +// int nativeMinor = Integer.parseInt(nativeVersionParts[1]); +// +// if(expectedMajor != nativeMajor) { +// return false; +// } +// +// if(expectedMinor > nativeMinor) { +// return false; +// } +// +// return true; +// } +// +// static { +// loadNativeDispatchLibrary(); +// +// /** @j2sNative +// * +// */ { +// if (!isCompatibleVersion(VERSION_NATIVE, getNativeVersion())) { +// String LS = System.lineSeparator(); +// throw new Error(LS + LS +// + "There is an incompatible JNA native library installed on this system" + LS +// + "Expected: " + VERSION_NATIVE + LS +// + "Found: " + getNativeVersion() + LS +// + (jnidispatchPath != null +// ? "(at " + jnidispatchPath + ")" : System.getProperty("java.library.path")) +// + "." + LS +// + "To resolve this issue you may do one of the following:" + LS +// + " - remove or uninstall the offending library" + LS +// + " - set the system property jna.nosys=true" + LS +// + " - set jna.boot.library.path to include the path to the version of the " + LS +// + " jnidispatch library included with the JNA jar file you are using" + LS); +// } +// +// POINTER_SIZE = sizeof(TYPE_VOIDP); +// LONG_SIZE = sizeof(TYPE_LONG); +// WCHAR_SIZE = sizeof(TYPE_WCHAR_T); +// SIZE_T_SIZE = sizeof(TYPE_SIZE_T); +// BOOL_SIZE = sizeof(TYPE_BOOL); +// LONG_DOUBLE_SIZE = sizeof(TYPE_LONG_DOUBLE); +// +// // Perform initialization of other JNA classes until *after* +// // initializing the above final fields +// initIDs(); +// if (Boolean.getBoolean("jna.protected")) { +// setProtected(true); +// } +// MAX_ALIGNMENT = Platform.isSPARC() || Platform.isWindows() +// || (Platform.isLinux() && (Platform.isARM() || Platform.isPPC() || Platform.isMIPS() || Platform.isLoongArch())) +// || Platform.isAIX() +// || (Platform.isAndroid() && !Platform.isIntel()) +// ? 8 : LONG_SIZE; +// MAX_PADDING = (Platform.isMac() && Platform.isPPC()) ? 8 : MAX_ALIGNMENT; +// System.setProperty("jna.loaded", "true"); +// } +// } +// +// /** Force a dispose when the Native class is GC'd. */ +// private static final Object finalizer = new Object() { +// @Override +// protected void finalize() throws Throwable { +// dispose(); +// super.finalize(); +// } +// }; +// +// /** Properly dispose of JNA functionality. +// Called when this class is finalized and also from JNI when +// JNA's native shared library is unloaded. +// */ +// private static void dispose() { +// CallbackReference.disposeAll(); +// Memory.disposeAll(); +// NativeLibrary.disposeAll(); +// unregisterAll(); +// jnidispatchPath = null; +// System.setProperty("jna.loaded", "false"); +// } +// +// /** Remove any automatically unpacked native library. +// +// This will fail on windows, which disallows removal of any file that is +// still in use, so an alternative is required in that case. Mark +// the file that could not be deleted, and attempt to delete any +// temporaries on next startup. +// +// Do NOT force the class loader to unload the native library, since +// that introduces issues with cleaning up any extant JNA bits +// (e.g. Memory) which may still need use of the library before shutdown. +// */ +// static boolean deleteLibrary(File lib) { +// if (lib.delete()) { +// return true; +// } +// +// // Couldn't delete it, mark for later deletion +// markTemporaryFile(lib); +// +// return false; +// } +// + private Native() { } + +// private static native void initIDs(); +// +// /** Set whether native memory accesses are protected from invalid +// * accesses. This should only be set true when testing or debugging, +// * and should not be considered reliable or robust for applications +// * where JNA native calls are occurring on multiple threads. +// * Protected mode will be automatically set if the +// * system property jna.protected has a value of "true" +// * when the JNA library is first loaded.

+// * If not supported by the underlying platform, this setting will +// * have no effect.

+// * NOTE: On platforms which support signals (non-Windows), JNA uses +// * signals to trap errors. This may interfere with the JVM's own use of +// * signals. When protected mode is enabled, you should make use of the +// * jsig library, if available (see Signal Chaining). +// * In short, set the environment variable LD_PRELOAD to the +// * path to libjsig.so in your JRE lib directory +// * (usually ${java.home}/lib/${os.arch}/libjsig.so) before launching your +// * Java application. +// */ +// public static synchronized native void setProtected(boolean enable); +// +// /** Returns whether protection is enabled. Check the result of this method +// * after calling {@link #setProtected setProtected(true)} to determine +// * if this platform supports protecting memory accesses. +// */ +// public static synchronized native boolean isProtected(); +// +// /** Utility method to get the native window ID for a Java {@link Window} +// * as a long value. +// * This method is primarily for X11-based systems, which use an opaque +// * XID (usually long int) to identify windows. +// * @throws HeadlessException if the current VM is running headless +// */ +// public static long getWindowID(Window w) throws HeadlessException { +// return AWT.getWindowID(w); +// } +// +// /** Utility method to get the native window ID for a heavyweight Java +// * {@link Component} as a long value. +// * This method is primarily for X11-based systems, which use an opaque +// * XID (usually long int) to identify windows. +// * @throws HeadlessException if the current VM is running headless +// */ +// public static long getComponentID(Component c) throws HeadlessException { +// return AWT.getComponentID(c); +// } +// +// /** Utility method to get the native window pointer for a Java +// * {@link Window} as a {@link Pointer} value. This method is primarily for +// * w32, which uses the HANDLE type (actually +// * void *) to identify windows. +// * @throws HeadlessException if the current VM is running headless +// */ +// public static Pointer getWindowPointer(Window w) throws HeadlessException { +// return new Pointer(AWT.getWindowID(w)); +// } +// +// /** Utility method to get the native window pointer for a heavyweight Java +// * {@link Component} as a {@link Pointer} value. This method is primarily +// * for w32, which uses the HWND type (actually +// * void *) to identify windows. +// * @throws HeadlessException if the current VM is running headless +// */ +// public static Pointer getComponentPointer(Component c) throws HeadlessException { +// return new Pointer(AWT.getComponentID(c)); +// } +// +// static native long getWindowHandle0(Component c); +// +// /** Convert a direct {@link Buffer} into a {@link Pointer}. +// * @throws IllegalArgumentException if the buffer is not direct. +// */ +// public static Pointer getDirectBufferPointer(Buffer b) { +// long peer = _getDirectBufferPointer(b); +// return peer == 0 ? null : new Pointer(peer); +// } +// +// private static native long _getDirectBufferPointer(Buffer b); +// +// /** +// * Gets the charset belonging to the given {@code encoding}. +// * @param encoding The encoding - if {@code null} then the default platform +// * encoding is used. +// * @return The charset belonging to the given {@code encoding} or the platform default. +// * Never {@code null}. +// */ +// private static Charset getCharset(String encoding) { +// Charset charset = null; +// if (encoding != null) { +// try { +// charset = Charset.forName(encoding); +// } +// catch(IllegalCharsetNameException | UnsupportedCharsetException e) { +// LOG.log(Level.WARNING, "JNA Warning: Encoding ''{0}'' is unsupported ({1})", +// new Object[]{encoding, e.getMessage()}); +// } +// } +// if (charset == null) { +// LOG.log(Level.WARNING, "JNA Warning: Using fallback encoding {0}", Native.DEFAULT_CHARSET); +// charset = Native.DEFAULT_CHARSET; +// } +// return charset; +// } +// +// /** +// * Obtain a Java String from the given native byte array. If there is +// * no NUL terminator, the String will comprise the entire array. The +// * encoding is obtained from {@link #getDefaultStringEncoding()}. +// * +// * @param buf The buffer containing the encoded bytes +// * @see #toString(byte[], String) +// */ +// public static String toString(byte[] buf) { +// return toString(buf, getDefaultStringEncoding()); +// } +// +// /** +// * Obtain a Java String from the given native byte array, using the given +// * encoding. If there is no NUL terminator, the String will comprise the +// * entire array. +// * +// *

Usage note: This function assumes, that {@code buf} +// * holds a {@code char} array. This means only single-byte encodings are +// * supported.

+// * +// * @param buf The buffer containing the encoded bytes. Must not be {@code null}. +// * @param encoding The encoding name - if {@code null} then the platform +// * default encoding will be used +// */ +// public static String toString(byte[] buf, String encoding) { +// return Native.toString(buf, Native.getCharset(encoding)); +// } +// +// /** +// * Obtain a Java String from the given native byte array, using the given +// * encoding. If there is no NUL terminator, the String will comprise the +// * entire array. +// * +// *

Usage note: This function assumes, that {@code buf} +// * holds a {@code char} array. This means only single-byte encodings are +// * supported.

+// * +// * @param buf The buffer containing the encoded bytes. Must not be {@code null}. +// * @param charset The charset to decode {@code buf}. Must not be {@code null}. +// */ +// public static String toString(byte[] buf, Charset charset) { +// int len = buf.length; +// // find out the effective length +// for (int index = 0; index < len; index++) { +// if (buf[index] == 0) { +// len = index; +// break; +// } +// } +// +// if (len == 0) { +// return ""; +// } +// +// return new String(buf, 0, len, charset); +// } +// +// /** +// * Obtain a Java String from the given native wchar_t array. If there is +// * no NUL terminator, the String will comprise the entire array. +// * +// * @param buf The buffer containing the characters +// */ +// public static String toString(char[] buf) { +// int len = buf.length; +// for (int index = 0; index < len; index++) { +// if (buf[index] == '\0') { +// len = index; +// break; +// } +// } +// +// if (len == 0) { +// return ""; +// } else { +// return new String(buf, 0, len); +// } +// } +// +// /** +// * Converts a "list" of strings each null terminated +// * into a {@link List} of {@link String} values. The end of the +// * list is signaled by an extra NULL value at the end or by the +// * end of the buffer. +// * @param buf The buffer containing the strings +// * @return A {@link List} of all the strings in the buffer +// * @see #toStringList(char[], int, int) +// */ +// public static List toStringList(char[] buf) { +// return toStringList(buf, 0, buf.length); +// } +// +// /** +// * Converts a "list" of strings each null terminated +// * into a {@link List} of {@link String} values. The end of the +// * list is signaled by an extra NULL value at the end or by the +// * end of the data. +// * @param buf The buffer containing the strings +// * @param offset Offset to start parsing +// * @param len The total characters to parse +// * @return A {@link List} of all the strings in the buffer +// */ +// public static List toStringList(char[] buf, int offset, int len) { +// List list = new ArrayList<>(); +// int lastPos = offset; +// int maxPos = offset + len; +// for (int curPos = offset; curPos < maxPos; curPos++) { +// if (buf[curPos] != '\0') { +// continue; +// } +// +// // check if found the extra null terminator +// if (lastPos == curPos) { +// return list; +// } +// +// String value = new String(buf, lastPos, curPos - lastPos); +// list.add(value); +// lastPos = curPos + 1; // skip the '\0' +// } +// +// // This point is reached if there is no double null terminator +// if (lastPos < maxPos) { +// String value = new String(buf, lastPos, maxPos - lastPos); +// list.add(value); +// } +// +// return list; +// } +// +// /** Map a library interface to the current process, providing +// * the explicit interface class. +// * Native libraries loaded via this method may be found in +// * several locations. +// * @param Type of expected wrapper +// * @param interfaceClass The implementation wrapper interface +// * @return an instance of the requested interface, mapped to the current +// * process. +// * @throws UnsatisfiedLinkError if the library cannot be found or +// * dependent libraries are missing. +// */ +// public static T load(Class interfaceClass) { +// return load(null, interfaceClass); +// } +// +// /** Map a library interface to the current process, providing +// * the explicit interface class. Any options provided for the library are +// * cached and associated with the library and any of its defined +// * structures and/or functions. +// * Native libraries loaded via this method may be found in +// * several locations. +// * @param Type of expected wrapper +// * @param interfaceClass The implementation wrapper interface +// * @param options Map of library options +// * @return an instance of the requested interface, mapped to the current +// * process. +// * @throws UnsatisfiedLinkError if the library cannot be found or +// * dependent libraries are missing. +// * @see #load(String, Class, Map) +// */ +// public static T load(Class interfaceClass, Map options) { +// return load(null, interfaceClass, options); +// } +// +// /** Map a library interface to the given shared library, providing +// * the explicit interface class. +// * If name is null, attempts to map onto the current process. +// * Native libraries loaded via this method may be found in +// * several locations. +// * @param Type of expected wrapper +// * @param name Library base name +// * @param interfaceClass The implementation wrapper interface +// * @return an instance of the requested interface, mapped to the indicated +// * native library. +// * @throws UnsatisfiedLinkError if the library cannot be found or +// * dependent libraries are missing. +// * @see #load(String, Class, Map) +// */ +// public static T load(String name, Class interfaceClass) { +// return load(name, interfaceClass, Collections.emptyMap()); +// } +// +// /** Load a library interface from the given shared library, providing +// * the explicit interface class and a map of options for the library. +// * If no library options are detected the map is interpreted as a map +// * of Java method names to native function names.

+// * If name is null, attempts to map onto the current process. +// * Native libraries loaded via this method may be found in +// * several locations. +// * @param Type of expected wrapper +// * @param name Library base name +// * @param interfaceClass The implementation wrapper interface +// * @param options Map of library options +// * @return an instance of the requested interface, mapped to the indicated +// * native library. +// * @throws UnsatisfiedLinkError if the library cannot be found or +// * dependent libraries are missing. +// */ +// public static T load(String name, Class interfaceClass, Map options) { +// if (!Library.class.isAssignableFrom(interfaceClass)) { +// // Maybe still possible if the caller is not using generics? +// throw new IllegalArgumentException("Interface (" + interfaceClass.getSimpleName() + ")" +// + " of library=" + name + " does not extend " + Library.class.getSimpleName()); +// } +// +// Library.Handler handler = new Library.Handler(name, interfaceClass, options); +// ClassLoader loader = interfaceClass.getClassLoader(); +// Object proxy = Proxy.newProxyInstance(loader, new Class[] {interfaceClass}, handler); +// cacheOptions(interfaceClass, options, proxy); +// return interfaceClass.cast(proxy); +// } +// +// /** +// * Provided for improved compatibility between JNA 4.X and 5.X +// * +// * @see Native#load(java.lang.Class) +// */ +// @Deprecated +// public static T loadLibrary(Class interfaceClass) { +// return loadLibrary(null, interfaceClass); +// } +// +// /** +// * Provided for improved compatibility between JNA 4.X and 5.X +// * +// * @see Native#load(java.lang.Class, java.util.Map) +// */ +// @Deprecated +// public static T loadLibrary(Class interfaceClass, Map options) { +// return loadLibrary(null, interfaceClass, options); +// } +// +// /** +// * Provided for improved compatibility between JNA 4.X and 5.X +// * +// * @see Native#load(java.lang.String, java.lang.Class) +// */ +// @Deprecated +// public static T loadLibrary(String name, Class interfaceClass) { +// return loadLibrary(name, interfaceClass, Collections.emptyMap()); +// } +// +// /** +// * Provided for improved compatibility between JNA 4.X and 5.X +// * +// * @see Native#load(java.lang.String, java.lang.Class, java.util.Map) +// */ +// @Deprecated +// public static T loadLibrary(String name, Class interfaceClass, Map options) { +// if (!Library.class.isAssignableFrom(interfaceClass)) { +// // Maybe still possible if the caller is not using generics? +// throw new IllegalArgumentException("Interface (" + interfaceClass.getSimpleName() + ")" +// + " of library=" + name + " does not extend " + Library.class.getSimpleName()); +// } +// +// Library.Handler handler = new Library.Handler(name, interfaceClass, options); +// ClassLoader loader = interfaceClass.getClassLoader(); +// Object proxy = Proxy.newProxyInstance(loader, new Class[] {interfaceClass}, handler); +// cacheOptions(interfaceClass, options, proxy); +// return interfaceClass.cast(proxy); +// } +// +// /** Attempts to force initialization of an instance of the library interface +// * by loading a public static field of the requisite type. +// * Returns whether an instance variable was instantiated. +// * Expects that lock on libraries is already held +// */ +// private static void loadLibraryInstance(Class cls) { +// if (cls != null && !libraries.containsKey(cls)) { +// try { +// Field[] fields = cls.getFields(); +// for (int i=0;i < fields.length;i++) { +// Field field = fields[i]; +// if (field.getType() == cls +// && Modifier.isStatic(field.getModifiers())) { +// // Ensure the field gets initialized by reading it +// field.setAccessible(true); // interface might be private +// libraries.put(cls, new WeakReference<>(field.get(null))); +// break; +// } +// } +// } +// catch (Exception e) { +// throw new IllegalArgumentException("Could not access instance of " +// + cls + " (" + e + ")"); +// } +// } +// } +// +// /** +// * Find the library interface corresponding to the given class. Checks +// * all ancestor classes and interfaces for a declaring class which +// * implements {@link Library}. +// * @param cls The given class +// * @return The enclosing class +// */ +// static Class findEnclosingLibraryClass(Class cls) { +// if (cls == null) { +// return null; +// } +// // Check for direct-mapped libraries, which won't necessarily +// // implement com.sun.jna.Library. +// Map libOptions = typeOptions.get(cls); +// if (libOptions != null) { +// Class enclosingClass = (Class)libOptions.get(_OPTION_ENCLOSING_LIBRARY); +// if (enclosingClass != null) { +// return enclosingClass; +// } +// return cls; +// } +// if (Library.class.isAssignableFrom(cls)) { +// return cls; +// } +// if (Callback.class.isAssignableFrom(cls)) { +// cls = CallbackReference.findCallbackClass(cls); +// } +// Class declaring = cls.getDeclaringClass(); +// Class fromDeclaring = findEnclosingLibraryClass(declaring); +// if (fromDeclaring != null) { +// return fromDeclaring; +// } +// return findEnclosingLibraryClass(cls.getSuperclass()); +// } +// +// +// /** Return the preferred native library configuration options for the given +// * class. First attempts to load any field of the interface type within +// * the interface mapping, then checks the cache for any specified library +// * options. If none found, a set of library options will be generated +// * from the fields (by order of precedence) OPTIONS (a {@link +// * Map}), TYPE_MAPPER (a {@link TypeMapper}), +// * STRUCTURE_ALIGNMENT (an {@link Integer}), and +// * STRING_ENCODING (a {@link String}). +// * +// * @param type The type class +// * @return The options map +// */ +// public static Map getLibraryOptions(Class type) { +// Map libraryOptions; +// // cached already ? +// libraryOptions = typeOptions.get(type); +// if (libraryOptions != null) { +// return libraryOptions; +// } +// +// Class mappingClass = findEnclosingLibraryClass(type); +// if (mappingClass != null) { +// loadLibraryInstance(mappingClass); +// } else { +// mappingClass = type; +// } +// +// libraryOptions = typeOptions.get(mappingClass); +// if (libraryOptions != null) { +// typeOptions.put(type, libraryOptions); // cache for next time +// return libraryOptions; +// } +// +// try { +// Field field = mappingClass.getField("OPTIONS"); +// field.setAccessible(true); +// libraryOptions = (Map) field.get(null); +// if (libraryOptions == null) { +// throw new IllegalStateException("Null options field"); +// } +// } catch (NoSuchFieldException e) { +// libraryOptions = Collections.emptyMap(); +// } catch (Exception e) { +// throw new IllegalArgumentException("OPTIONS must be a public field of type java.util.Map (" + e + "): " + mappingClass); +// } +// // Make a clone of the original options +// libraryOptions = new HashMap<>(libraryOptions); +// if (!libraryOptions.containsKey(Library.OPTION_TYPE_MAPPER)) { +// libraryOptions.put(Library.OPTION_TYPE_MAPPER, lookupField(mappingClass, "TYPE_MAPPER", TypeMapper.class)); +// } +// if (!libraryOptions.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT)) { +// libraryOptions.put(Library.OPTION_STRUCTURE_ALIGNMENT, lookupField(mappingClass, "STRUCTURE_ALIGNMENT", Integer.class)); +// } +// if (!libraryOptions.containsKey(Library.OPTION_STRING_ENCODING)) { +// libraryOptions.put(Library.OPTION_STRING_ENCODING, lookupField(mappingClass, "STRING_ENCODING", String.class)); +// } +// libraryOptions = cacheOptions(mappingClass, libraryOptions, null); +// // Store the original lookup class, if different from the mapping class +// if (type != mappingClass) { +// typeOptions.put(type, libraryOptions); +// } +// return libraryOptions; +// } +// +// private static Object lookupField(Class mappingClass, String fieldName, Class resultClass) { +// try { +// Field field = mappingClass.getField(fieldName); +// field.setAccessible(true); +// return field.get(null); +// } +// catch (NoSuchFieldException e) { +// return null; +// } +// catch (Exception e) { +// throw new IllegalArgumentException(fieldName + " must be a public field of type " +// + resultClass.getName() + " (" +// + e + "): " + mappingClass); +// } +// } +// +// /** Return the preferred {@link TypeMapper} for the given native interface. +// * See {@link com.sun.jna.Library#OPTION_TYPE_MAPPER}. +// */ +// public static TypeMapper getTypeMapper(Class cls) { +// Map options = getLibraryOptions(cls); +// return (TypeMapper) options.get(Library.OPTION_TYPE_MAPPER); +// } +// +// /** +// * @param cls The native interface type +// * @return The preferred string encoding for the given native interface. +// * If there is no setting, defaults to the {@link #getDefaultStringEncoding()}. +// * @see com.sun.jna.Library#OPTION_STRING_ENCODING +// */ +// public static String getStringEncoding(Class cls) { +// Map options = getLibraryOptions(cls); +// String encoding = (String) options.get(Library.OPTION_STRING_ENCODING); +// return encoding != null ? encoding : getDefaultStringEncoding(); +// } +// +// /** +// * @return The default string encoding. Returns the value of the system +// * property jna.encoding or {@link Native#DEFAULT_ENCODING}. +// */ +// public static String getDefaultStringEncoding() { +// return System.getProperty("jna.encoding", DEFAULT_ENCODING); +// } +// +// /** +// * @param cls The native interface type +// * @return The preferred structure alignment for the given native interface. +// * @see com.sun.jna.Library#OPTION_STRUCTURE_ALIGNMENT +// */ +// public static int getStructureAlignment(Class cls) { +// Integer alignment = (Integer)getLibraryOptions(cls).get(Library.OPTION_STRUCTURE_ALIGNMENT); +// return alignment == null ? Structure.ALIGN_DEFAULT : alignment; +// } +// +// /** +// * @param s The input string +// * @return A byte array corresponding to the given String. The encoding +// * used is obtained from {@link #getDefaultStringEncoding()}. +// */ +// static byte[] getBytes(String s) { +// return getBytes(s, getDefaultStringEncoding()); +// } +// +// /** +// * @param s The string. Must not be {@code null}. +// * @param encoding The encoding - if {@code null} then the default platform +// * encoding is used +// * @return A byte array corresponding to the given String, using the given +// * encoding. If the encoding is not found default to the platform native +// * encoding. +// */ +// static byte[] getBytes(String s, String encoding) { +// return Native.getBytes(s, Native.getCharset(encoding)); +// } +// +// /** +// * @param s The string. Must not be {@code null}. +// * @param charset The charset used to encode {@code s}. Must not be {@code null}. +// * @return A byte array corresponding to the given String, using the given +// * charset. +// */ +// static byte[] getBytes(String s, Charset charset) { +// return s.getBytes(charset); +// } +// +// /** +// * @param s The string +// * @return A NUL-terminated byte buffer equivalent to the given String, +// * using the encoding returned by {@link #getDefaultStringEncoding()}. +// * @see #toByteArray(String, String) +// */ +// public static byte[] toByteArray(String s) { +// return toByteArray(s, getDefaultStringEncoding()); +// } +// +// /** +// * @param s The string. Must not be {@code null}. +// * @param encoding The encoding - if {@code null} then the default platform +// * encoding is used +// * @return A NUL-terminated byte buffer equivalent to the given String, +// * using the given encoding. +// * @see #getBytes(String, String) +// */ +// public static byte[] toByteArray(String s, String encoding) { +// return Native.toByteArray(s, Native.getCharset(encoding)); +// } +// +// /** +// * @param s The string. Must not be {@code null}. +// * @param charset The charset used to encode {@code s}. Must not be {@code null}. +// * @return A NUL-terminated byte buffer equivalent to the given String, +// * using the given charset. +// * @see #getBytes(String, String) +// */ +// public static byte[] toByteArray(String s, Charset charset) { +// byte[] bytes = Native.getBytes(s, charset); +// byte[] buf = new byte[bytes.length+1]; +// System.arraycopy(bytes, 0, buf, 0, bytes.length); +// return buf; +// } +// +// /** +// * @param s The string +// * @return A NUL-terminated wide character buffer equivalent to the given string. +// */ +// public static char[] toCharArray(String s) { +// char[] chars = s.toCharArray(); +// char[] buf = new char[chars.length+1]; +// System.arraycopy(chars, 0, buf, 0, chars.length); +// return buf; +// } +// +// /** +// * Loads the JNA stub library. +// * First tries jna.boot.library.path, then the system path, then from the +// * jar file. +// */ +// private static void loadNativeDispatchLibrary() { +// /** +// * @j2sNative +// */ +// { +// if (!Boolean.getBoolean("jna.nounpack")) { +// try { +// removeTemporaryFiles(); +// } +// catch(IOException e) { +// LOG.log(Level.WARNING, "JNA Warning: IOException removing temporary files", e); +// } +// } +// +// String libName = System.getProperty("jna.boot.library.name", "jnidispatch"); +// String bootPath = System.getProperty("jna.boot.library.path"); +// if (bootPath != null) { +// // String.split not available in 1.4 +// StringTokenizer dirs = new StringTokenizer(bootPath, File.pathSeparator); +// while (dirs.hasMoreTokens()) { +// String dir = dirs.nextToken(); +// File file = new File(new File(dir), System.mapLibraryName(libName).replace(".dylib", ".jnilib")); +// String path = file.getAbsolutePath(); +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Looking in {0}", path); +// if (file.exists()) { +// try { +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", path); +// System.setProperty("jnidispatch.path", path); +// System.load(path); +// jnidispatchPath = path; +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", path); +// return; +// } catch (UnsatisfiedLinkError ex) { +// // Not a problem if already loaded in anoteher class loader +// // Unfortunately we can't distinguish the difference... +// //System.out.println("File found at " + file + " but not loadable: " + ex.getMessage()); +// } +// } +// if (Platform.isMac()) { +// String orig, ext; +// if (path.endsWith("dylib")) { +// orig = "dylib"; +// ext = "jnilib"; +// } else { +// orig = "jnilib"; +// ext = "dylib"; +// } +// path = path.substring(0, path.lastIndexOf(orig)) + ext; +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Looking in {0}", path); +// if (new File(path).exists()) { +// try { +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", path); +// System.setProperty("jnidispatch.path", path); +// System.load(path); +// jnidispatchPath = path; +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", path); +// return; +// } catch (UnsatisfiedLinkError ex) { +// LOG.log(Level.WARNING, "File found at " + path + " but not loadable: " + ex.getMessage(), ex); +// } +// } +// } +// } +// } +// String jnaNosys = System.getProperty("jna.nosys", "true"); +// if ((!Boolean.parseBoolean(jnaNosys)) || Platform.isAndroid()) { +// try { +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying (via loadLibrary) {0}", libName); +// System.loadLibrary(libName); +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch on system path"); +// return; +// } +// catch(UnsatisfiedLinkError e) { +// } +// } +// if (!Boolean.getBoolean("jna.noclasspath")) { +// loadNativeDispatchLibraryFromClasspath(); +// } +// else { +// throw new UnsatisfiedLinkError("Unable to locate JNA native support library"); +// } +// } +// } +// +// static final String JNA_TMPLIB_PREFIX = "jna"; +// /** +// * Attempts to load the native library resource from the filesystem, +// * extracting the JNA stub library from jna.jar if not already available. +// */ +// private static void loadNativeDispatchLibraryFromClasspath() { +// try { +// String mappedName = System.mapLibraryName("jnidispatch").replace(".dylib", ".jnilib"); +// if(Platform.isAIX()) { +// // OpenJDK is reported to map to .so -- this works around the +// // difference between J9 and OpenJDK +// mappedName = "libjnidispatch.a"; +// } +// String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + mappedName; +// File lib = extractFromResourcePath(libName, Native.class.getClassLoader()); +// if (lib == null) { +// if (lib == null) { +// throw new UnsatisfiedLinkError("Could not find JNA native support"); +// } +// } +// +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", lib.getAbsolutePath()); +// System.setProperty("jnidispatch.path", lib.getAbsolutePath()); +// System.load(lib.getAbsolutePath()); +// jnidispatchPath = lib.getAbsolutePath(); +// LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", jnidispatchPath); +// +// // Attempt to delete immediately once jnidispatch is successfully +// // loaded. This avoids the complexity of trying to do so on "exit", +// // which point can vary under different circumstances (native +// // compilation, dynamically loaded modules, normal application, etc). +// if (isUnpacked(lib) +// && !Boolean.getBoolean("jnidispatch.preserve")) { +// deleteLibrary(lib); +// } +// } +// catch(IOException e) { +// throw new UnsatisfiedLinkError(e.getMessage()); +// } +// } +// +// /** Identify temporary files unpacked from classpath jar files. */ +// static boolean isUnpacked(File file) { +// return file.getName().startsWith(JNA_TMPLIB_PREFIX); +// } +// +// /** Attempt to extract a native library from the current resource path, +// * using the current thread context class loader. +// * @param name Base name of native library to extract. May also be an +// * absolute resource path (i.e. starts with "/"), in which case the +// * no transformations of the library name are performed. If only the base +// * name is given, the resource path is attempted both with and without +// * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via +// * {@link NativeLibrary#mapSharedLibraryName(String)}. +// * @return File indicating extracted resource on disk +// * @throws IOException if resource not found +// */ +// public static File extractFromResourcePath(String name) throws IOException { +// return extractFromResourcePath(name, null); +// } +// +// /** Attempt to extract a native library from the resource path using the +// * given class loader. +// * @param name Base name of native library to extract. May also be an +// * absolute resource path (i.e. starts with "/"), in which case the +// * no transformations of the library name are performed. If only the base +// * name is given, the resource path is attempted both with and without +// * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via +// * {@link NativeLibrary#mapSharedLibraryName(String)}. +// * @param loader Class loader to use to load resources +// * @return File indicating extracted resource on disk +// * @throws IOException if resource not found +// */ +// public static File extractFromResourcePath(String name, ClassLoader loader) throws IOException { +// +// final Level DEBUG = (DEBUG_LOAD +// || (DEBUG_JNA_LOAD && name.contains("jnidispatch"))) ? Level.INFO : Level.FINE; +// if (loader == null) { +// loader = Thread.currentThread().getContextClassLoader(); +// // Context class loader is not guaranteed to be set +// if (loader == null) { +// loader = Native.class.getClassLoader(); +// } +// } +// LOG.log(DEBUG, "Looking in classpath from {0} for {1}", new Object[]{loader, name}); +// String libname = name.startsWith("/") ? name : NativeLibrary.mapSharedLibraryName(name); +// String resourcePath = name.startsWith("/") ? name : Platform.RESOURCE_PREFIX + "/" + libname; +// if (resourcePath.startsWith("/")) { +// resourcePath = resourcePath.substring(1); +// } +// URL url = loader.getResource(resourcePath); +// if (url == null) { +// if (resourcePath.startsWith(Platform.RESOURCE_PREFIX)) { +// // Fallback for legacy darwin behaviour: darwin was in the past +// // special cased in that all architectures were mapped to the same +// // prefix and it was expected, that a fat binary was present at that +// // point, that contained all architectures. +// if(Platform.RESOURCE_PREFIX.startsWith("darwin")) { +// url = loader.getResource("darwin/" + resourcePath.substring(Platform.RESOURCE_PREFIX.length() + 1)); +// } +// if (url == null) { +// // If not found with the standard resource prefix, try without it +// url = loader.getResource(libname); +// } +// } else if (resourcePath.startsWith("com/sun/jna/" + Platform.RESOURCE_PREFIX + "/")) { +// // Fallback for legacy darwin behaviour: darwin was in the past +// // special cased in that all architectures were mapped to the same +// // prefix and it was expected, that a fat binary was present at that +// // point, that contained all architectures. +// if(Platform.RESOURCE_PREFIX.startsWith("com/sun/jna/darwin")) { +// url = loader.getResource("com/sun/jna/darwin" + resourcePath.substring(("com/sun/jna/" + Platform.RESOURCE_PREFIX).length() + 1)); +// } +// if (url == null) { +// // If not found with the standard resource prefix, try without it +// url = loader.getResource(libname); +// } +// } +// } +// if (url == null) { +// String path = System.getProperty("java.class.path"); +// if (loader instanceof URLClassLoader) { +// path = Arrays.asList(((URLClassLoader)loader).getURLs()).toString(); +// } +// throw new IOException("Native library (" + resourcePath + ") not found in resource path (" + path + ")"); +// } +// LOG.log(DEBUG, "Found library resource at {0}", url); +// +// File lib = null; +// if (url.getProtocol().toLowerCase().equals("file")) { +// try { +// lib = new File(new URI(url.toString())); +// } +// catch(URISyntaxException e) { +// lib = new File(url.getPath()); +// } +// LOG.log(DEBUG, "Looking in {0}", lib.getAbsolutePath()); +// if (!lib.exists()) { +// throw new IOException("File URL " + url + " could not be properly decoded"); +// } +// } +// else if (!Boolean.getBoolean("jna.nounpack")) { +// InputStream is = url.openStream(); +// if (is == null) { +// throw new IOException("Can't obtain InputStream for " + resourcePath); +// } +// +// FileOutputStream fos = null; +// try { +// // Suffix is required on windows, or library fails to load +// // Let Java pick the suffix, except on windows, to avoid +// // problems with Web Start. +// File dir = getTempDir(); +// lib = File.createTempFile(JNA_TMPLIB_PREFIX, Platform.isWindows()?".dll":null, dir); +// if (!Boolean.getBoolean("jnidispatch.preserve")) { +// lib.deleteOnExit(); +// } +// LOG.log(DEBUG, "Extracting library to {0}", lib.getAbsolutePath()); +// fos = new FileOutputStream(lib); +// int count; +// byte[] buf = new byte[1024]; +// while ((count = is.read(buf, 0, buf.length)) > 0) { +// fos.write(buf, 0, count); +// } +// } +// catch(IOException e) { +// throw new IOException("Failed to create temporary file for " + name + " library: " + e.getMessage()); +// } +// finally { +// try { is.close(); } catch(IOException e) { } +// if (fos != null) { +// try { fos.close(); } catch(IOException e) { } +// } +// } +// } +// return lib; +// } +// +// /** +// * Initialize field and method IDs for native methods of this class. +// * Returns the size of a native pointer. +// **/ +// private static native int sizeof(int type); +// +// private static native String getNativeVersion(); +// private static native String getAPIChecksum(); +// +// /** Retrieve last error set by the OS. This corresponds to +// * GetLastError() on Windows, and errno on +// * most other platforms. The value is preserved per-thread, but whether +// * the original value is per-thread depends on the underlying OS. +// *

+// * An alternative method of obtaining the last error result is +// * to declare your mapped method to throw {@link LastErrorException} +// * instead. If a method's signature includes a throw of {@link +// * LastErrorException}, the last error will be set to zero before the +// * native call and a {@link LastErrorException} will be raised if the last +// * error value is non-zero after the call, regardless of the actual +// * returned value from the native function.

+// */ +// public static native int getLastError(); +// +// /** Set the OS last error code. The value will be saved on a per-thread +// * basis. +// */ +// public static native void setLastError(int code); +// +// /** +// * Returns a synchronized (thread-safe) library backed by the specified +// * library. This wrapping will prevent simultaneous invocations of any +// * functions mapped to a given {@link NativeLibrary}. Note that the +// * native library may still be sensitive to being called from different +// * threads. +// *

+// * @param library the library to be "wrapped" in a synchronized library. +// * @return a synchronized view of the specified library. +// */ +// public static Library synchronizedLibrary(final Library library) { +// Class cls = library.getClass(); +// if (!Proxy.isProxyClass(cls)) { +// throw new IllegalArgumentException("Library must be a proxy class"); +// } +// InvocationHandler ih = Proxy.getInvocationHandler(library); +// if (!(ih instanceof Library.Handler)) { +// throw new IllegalArgumentException("Unrecognized proxy handler: " + ih); +// } +// final Library.Handler handler = (Library.Handler)ih; +// InvocationHandler newHandler = new InvocationHandler() { +// @Override +// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { +// synchronized(handler.getNativeLibrary()) { +// return handler.invoke(library, method, args); +// } +// } +// }; +// return (Library)Proxy.newProxyInstance(cls.getClassLoader(), +// cls.getInterfaces(), +// newHandler); +// } +// +// /** If running web start, determine the location of a given native +// * library. This value may be used to properly set +// * jna.library.path so that JNA can load libraries identified +// * by the <nativelib> tag in the JNLP configuration file. Returns +// * null if the Web Start native library cache location can not +// * be determined. Note that the path returned may be different for any +// * given library name. +// *

+// * Use System.getProperty("javawebstart.version") to detect +// * whether your code is running under Web Start. +// * @throws UnsatisfiedLinkError if the library can't be found by the +// * Web Start class loader, which usually means it wasn't included as +// * a <nativelib> resource in the JNLP file. +// * @return null if unable to query the web start loader. +// */ +// public static String getWebStartLibraryPath(final String libName) { +// if (System.getProperty("javawebstart.version") == null) +// return null; +// try { +// +// final ClassLoader cl = Native.class.getClassLoader(); +// Method m = AccessController.doPrivileged(new PrivilegedAction() { +// @Override +// public Method run() { +// try { +// Method m = ClassLoader.class.getDeclaredMethod("findLibrary", new Class[] { String.class }); +// m.setAccessible(true); +// return m; +// } +// catch(Exception e) { +// return null; +// } +// } +// }); +// String libpath = (String)m.invoke(cl, new Object[] { libName }); +// if (libpath != null) { +// return new File(libpath).getParent(); +// } +// return null; +// } +// catch (Exception e) { +// return null; +// } +// } +// +// /** Perform cleanup of automatically unpacked native shared library. +// */ +// static void markTemporaryFile(File file) { +// // If we can't force an unload/delete, flag the file for later +// // deletion +// try { +// File marker = new File(file.getParentFile(), file.getName() + ".x"); +// marker.createNewFile(); +// } +// catch(IOException e) { e.printStackTrace(); } +// } +// +// /** Obtain a directory suitable for writing JNA-specific temporary files. +// Override with jna.tmpdir +// */ +// static File getTempDir() throws IOException { +// File jnatmp; +// String prop = System.getProperty("jna.tmpdir"); +// if (prop != null) { +// jnatmp = new File(prop); +// jnatmp.mkdirs(); +// } +// else { +// File tmp = new File(System.getProperty("java.io.tmpdir")); +// if(Platform.isMac()) { +// // https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html +// jnatmp = new File(System.getProperty("user.home"), "Library/Caches/JNA/temp"); +// } else if (Platform.isLinux() || Platform.isSolaris() || Platform.isAIX() || Platform.isDragonFlyBSD() || Platform.isFreeBSD() || Platform.isNetBSD() || Platform.isOpenBSD() || Platform.iskFreeBSD()) { +// // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html +// // The XDG_CACHE_DIR is expected to be per user +// String xdgCacheEnvironment = System.getenv("XDG_CACHE_HOME"); +// File xdgCacheFile; +// if(xdgCacheEnvironment == null || xdgCacheEnvironment.trim().isEmpty()) { +// xdgCacheFile = new File(System.getProperty("user.home"), ".cache"); +// } else { +// xdgCacheFile = new File(xdgCacheEnvironment); +// } +// jnatmp = new File(xdgCacheFile, "JNA/temp"); +// } else { +// // Loading DLLs via System.load() under a directory with a unicode +// // name will fail on windows, so use a hash code of the user's +// // name in case the user's name contains non-ASCII characters +// jnatmp = new File(tmp, "jna-" + System.getProperty("user.name").hashCode()); +// } +// +// jnatmp.mkdirs(); +// if (!jnatmp.exists() || !jnatmp.canWrite()) { +// jnatmp = tmp; +// } +// } +// if (!jnatmp.exists()) { +// throw new IOException("JNA temporary directory '" + jnatmp + "' does not exist"); +// } +// if (!jnatmp.canWrite()) { +// throw new IOException("JNA temporary directory '" + jnatmp + "' is not writable"); +// } +// return jnatmp; +// } +// +// /** Remove all marked temporary files in the given directory. */ +// static void removeTemporaryFiles() throws IOException { +// File dir = getTempDir(); +// FilenameFilter filter = new FilenameFilter() { +// @Override +// public boolean accept(File dir, String name) { +// return name.endsWith(".x") && name.startsWith(JNA_TMPLIB_PREFIX); +// } +// }; +// File[] files = dir.listFiles(filter); +// for (int i=0;files != null && i < files.length;i++) { +// File marker = files[i]; +// String name = marker.getName(); +// name = name.substring(0, name.length()-2); +// File target = new File(marker.getParentFile(), name); +// if (!target.exists() || target.delete()) { +// marker.delete(); +// } +// } +// } +// +// /** +// * @param type The Java class for which the native size is to be determined +// * @param value an instance of said class (if available) +// * @return the native size of the given class, in bytes. +// * For use with arrays. +// */ +// public static int getNativeSize(Class type, Object value) { +// if (type.isArray()) { +// int len = Array.getLength(value); +// if (len > 0) { +// Object o = Array.get(value, 0); +// return len * getNativeSize(type.getComponentType(), o); +// } +// // Don't process zero-length arrays +// throw new IllegalArgumentException("Arrays of length zero not allowed: " + type); +// } +// if (Structure.class.isAssignableFrom(type) +// && !Structure.ByReference.class.isAssignableFrom(type)) { +// return Structure.size((Class) type, (Structure)value); +// } +// try { +// return getNativeSize(type); +// } +// catch(IllegalArgumentException e) { +// throw new IllegalArgumentException("The type \"" + type.getName() +// + "\" is not supported: " +// + e.getMessage()); +// } +// } +// +// /** +// * Returns the native size for a given Java class. Structures are +// * assumed to be struct pointers unless they implement +// * {@link Structure.ByValue}. +// * +// * @param cls The Java class +// * @return The native size for the class +// */ +// public static int getNativeSize(Class cls) { +// if (NativeMapped.class.isAssignableFrom(cls)) { +// cls = NativeMappedConverter.getInstance(cls).nativeType(); +// } +// // boolean defaults to 32 bit integer if not otherwise mapped +// if (cls == boolean.class || cls == Boolean.class) return 4; +// if (cls == byte.class || cls == Byte.class) return 1; +// if (cls == short.class || cls == Short.class) return 2; +// if (cls == char.class || cls == Character.class) return WCHAR_SIZE; +// if (cls == int.class || cls == Integer.class) return 4; +// if (cls == long.class || cls == Long.class) return 8; +// if (cls == float.class || cls == Float.class) return 4; +// if (cls == double.class || cls == Double.class) return 8; +// if (Structure.class.isAssignableFrom(cls)) { +// if (Structure.ByValue.class.isAssignableFrom(cls)) { +// return Structure.size((Class) cls); +// } +// return POINTER_SIZE; +// } +// if (Pointer.class.isAssignableFrom(cls) +// || (Platform.HAS_BUFFERS && Buffers.isBuffer(cls)) +// || Callback.class.isAssignableFrom(cls) +// || String.class == cls +// || WString.class == cls) { +// return POINTER_SIZE; +// } +// throw new IllegalArgumentException("Native size for type \"" + cls.getName() +// + "\" is unknown"); +// } +// +// /** +// * @param cls The Java class +// * @return {@code true} whether the given class is supported as a native argument type. +// */ +// public static boolean isSupportedNativeType(Class cls) { +// if (Structure.class.isAssignableFrom(cls)) { +// return true; +// } +// try { +// return getNativeSize(cls) != 0; +// } +// catch(IllegalArgumentException e) { +// return false; +// } +// } +// +// /** +// * Set the default handler invoked when a callback throws an uncaught +// * exception. If the given handler is null, the default +// * handler will be reinstated. +// * +// * @param eh The default handler +// */ +// public static void setCallbackExceptionHandler(UncaughtExceptionHandler eh) { +// callbackExceptionHandler = eh == null ? DEFAULT_HANDLER : eh; +// } +// +// /** @return the current handler for callback uncaught exceptions. */ +// public static UncaughtExceptionHandler getCallbackExceptionHandler() { +// return callbackExceptionHandler; +// } + +// /** +// * When called from a class static initializer, maps all native methods +// * found within that class to native libraries via the JNA raw calling +// * interface. +// * @param libName library name to which functions should be bound +// */ +// public static void register(String libName) { +// register(findDirectMappedClass(getCallingClass()), libName); +// } +// +// /** +// * When called from a class static initializer, maps all native methods +// * found within that class to native libraries via the JNA raw calling +// * interface. +// * @param lib native library to which functions should be bound +// */ +// public static void register(NativeLibrary lib) { +// register(findDirectMappedClass(getCallingClass()), lib); +// } +// +// /** Find the nearest enclosing class with native methods. */ +// static Class findDirectMappedClass(Class cls) { +// Method[] methods = cls.getDeclaredMethods(); +// for (Method m : methods) { +// if ((m.getModifiers() & Modifier.NATIVE) != 0) { +// return cls; +// } +// } +// int idx = cls.getName().lastIndexOf("$"); +// if (idx != -1) { +// String name = cls.getName().substring(0, idx); +// try { +// return findDirectMappedClass(Class.forName(name, true, cls.getClassLoader())); +// } catch(ClassNotFoundException e) { +// // ignored +// } +// } +// throw new IllegalArgumentException("Can't determine class with native methods from the current context (" + cls + ")"); +// } +// +// /** Try to determine the class context in which a {@link #register(String)} call +// was made. +// */ +// static Class getCallingClass() { +// Class[] context = new SecurityManager() { +// @Override +// public Class[] getClassContext() { +// return super.getClassContext(); +// } +// }.getClassContext(); +// if (context == null) { +// throw new IllegalStateException("The SecurityManager implementation on this platform is broken; you must explicitly provide the class to register"); +// } +// if (context.length < 4) { +// throw new IllegalStateException("This method must be called from the static initializer of a class"); +// } +// return context[3]; +// } +// +// /** +// * Set a thread initializer for the given callback. +// * @param cb The callback to invoke +// * @param initializer The thread initializer indicates desired thread configuration when the +// * given Callback is invoked on a native thread not yet attached to the VM. +// */ +// public static void setCallbackThreadInitializer(Callback cb, CallbackThreadInitializer initializer) { +// CallbackReference.setCallbackThreadInitializer(cb, initializer); +// } +// +// private static final Map, long[]> registeredClasses = new WeakHashMap<>(); +// private static final Map, NativeLibrary> registeredLibraries = new WeakHashMap<>(); +// +// private static void unregisterAll() { +// synchronized(registeredClasses) { +// for (Map.Entry, long[]> e : registeredClasses.entrySet()) { +// unregister(e.getKey(), e.getValue()); +// } +// +// registeredClasses.clear(); +// } +// } +// +// /** Remove all native mappings for the calling class. +// Should only be called if the class is no longer referenced and about +// to be garbage collected. +// */ +// public static void unregister() { +// unregister(findDirectMappedClass(getCallingClass())); +// } +// +// /** Remove all native mappings for the given class. +// Should only be called if the class is no longer referenced and about +// to be garbage collected. +// */ + public static void unregister(Class cls) { +// synchronized(registeredClasses) { +// long[] handles = registeredClasses.get(cls); +// if (handles != null) { +// unregister(cls, handles); +// registeredClasses.remove(cls); +// registeredLibraries.remove(cls); +// } +// } + } +// +// /** +// * @param cls The type {@link Class} +// * @return whether the given class's native components are registered. +// */ +// public static boolean registered(Class cls) { +// synchronized(registeredClasses) { +// return registeredClasses.containsKey(cls); +// } +// } +// +// /* Unregister the native methods for the given class. */ +// private static native void unregister(Class cls, long[] handles); +// +// static String getSignature(Class cls) { +// if (cls.isArray()) { +// return "[" + getSignature(cls.getComponentType()); +// } +// if (cls.isPrimitive()) { +// if (cls == void.class) return "V"; +// if (cls == boolean.class) return "Z"; +// if (cls == byte.class) return "B"; +// if (cls == short.class) return "S"; +// if (cls == char.class) return "C"; +// if (cls == int.class) return "I"; +// if (cls == long.class) return "J"; +// if (cls == float.class) return "F"; +// if (cls == double.class) return "D"; +// } +// return "L" + replace(".", "/", cls.getName()) + ";"; +// } +// +// // No String.replace available in 1.4 +// static String replace(String s1, String s2, String str) { +// StringBuilder buf = new StringBuilder(); +// while (true) { +// int idx = str.indexOf(s1); +// if (idx == -1) { +// buf.append(str); +// break; +// } +// else { +// buf.append(str.substring(0, idx)); +// buf.append(s2); +// str = str.substring(idx + s1.length()); +// } +// } +// return buf.toString(); +// } +// +// /** Indicates whether the callback has an initializer. */ +// static final int CB_HAS_INITIALIZER = 1; +// +// private static final int CVT_UNSUPPORTED = -1; +// private static final int CVT_DEFAULT = 0; +// private static final int CVT_POINTER = 1; +// private static final int CVT_STRING = 2; +// private static final int CVT_STRUCTURE = 3; +// private static final int CVT_STRUCTURE_BYVAL = 4; +// private static final int CVT_BUFFER = 5; +// private static final int CVT_ARRAY_BYTE = 6; +// private static final int CVT_ARRAY_SHORT = 7; +// private static final int CVT_ARRAY_CHAR = 8; +// private static final int CVT_ARRAY_INT = 9; +// private static final int CVT_ARRAY_LONG = 10; +// private static final int CVT_ARRAY_FLOAT = 11; +// private static final int CVT_ARRAY_DOUBLE = 12; +// private static final int CVT_ARRAY_BOOLEAN = 13; +// private static final int CVT_BOOLEAN = 14; +// private static final int CVT_CALLBACK = 15; +// private static final int CVT_FLOAT = 16; +// private static final int CVT_NATIVE_MAPPED = 17; +// private static final int CVT_NATIVE_MAPPED_STRING = 18; +// private static final int CVT_NATIVE_MAPPED_WSTRING = 19; +// private static final int CVT_WSTRING = 20; +// private static final int CVT_INTEGER_TYPE = 21; +// private static final int CVT_POINTER_TYPE = 22; +// private static final int CVT_TYPE_MAPPER = 23; +// private static final int CVT_TYPE_MAPPER_STRING = 24; +// private static final int CVT_TYPE_MAPPER_WSTRING = 25; +// private static final int CVT_OBJECT = 26; +// private static final int CVT_JNIENV = 27; +// private static final int CVT_SHORT = 28; +// private static final int CVT_BYTE = 29; +// +// private static int getConversion(Class type, TypeMapper mapper, boolean allowObjects) { +// if (type == Void.class) type = void.class; +// +// if (mapper != null) { +// FromNativeConverter fromNative = mapper.getFromNativeConverter(type); +// ToNativeConverter toNative = mapper.getToNativeConverter(type); +// if (fromNative != null) { +// Class nativeType = fromNative.nativeType(); +// if (nativeType == String.class) { +// return CVT_TYPE_MAPPER_STRING; +// } +// if (nativeType == WString.class) { +// return CVT_TYPE_MAPPER_WSTRING; +// } +// return CVT_TYPE_MAPPER; +// } +// if (toNative != null) { +// Class nativeType = toNative.nativeType(); +// if (nativeType == String.class) { +// return CVT_TYPE_MAPPER_STRING; +// } +// if (nativeType == WString.class) { +// return CVT_TYPE_MAPPER_WSTRING; +// } +// return CVT_TYPE_MAPPER; +// } +// } +// +// if (Pointer.class.isAssignableFrom(type)) { +// return CVT_POINTER; +// } +// if (String.class == type) { +// return CVT_STRING; +// } +// if (WString.class.isAssignableFrom(type)) { +// return CVT_WSTRING; +// } +// if (Platform.HAS_BUFFERS && Buffers.isBuffer(type)) { +// return CVT_BUFFER; +// } +// if (Structure.class.isAssignableFrom(type)) { +// if (Structure.ByValue.class.isAssignableFrom(type)) { +// return CVT_STRUCTURE_BYVAL; +// } +// return CVT_STRUCTURE; +// } +// if (type.isArray()) { +// switch(type.getName().charAt(1)) { +// case 'Z': return CVT_ARRAY_BOOLEAN; +// case 'B': return CVT_ARRAY_BYTE; +// case 'S': return CVT_ARRAY_SHORT; +// case 'C': return CVT_ARRAY_CHAR; +// case 'I': return CVT_ARRAY_INT; +// case 'J': return CVT_ARRAY_LONG; +// case 'F': return CVT_ARRAY_FLOAT; +// case 'D': return CVT_ARRAY_DOUBLE; +// default: break; +// } +// } +// if (type.isPrimitive()) { +// return type == boolean.class ? CVT_BOOLEAN : CVT_DEFAULT; +// } +// if (Callback.class.isAssignableFrom(type)) { +// return CVT_CALLBACK; +// } +// if (IntegerType.class.isAssignableFrom(type)) { +// return CVT_INTEGER_TYPE; +// } +// if (PointerType.class.isAssignableFrom(type)) { +// return CVT_POINTER_TYPE; +// } +// if (NativeMapped.class.isAssignableFrom(type)) { +// Class nativeType = NativeMappedConverter.getInstance(type).nativeType(); +// if (nativeType == String.class) { +// return CVT_NATIVE_MAPPED_STRING; +// } +// if (nativeType == WString.class) { +// return CVT_NATIVE_MAPPED_WSTRING; +// } +// return CVT_NATIVE_MAPPED; +// } +// if (JNIEnv.class == type) { +// return CVT_JNIENV; +// } +// return allowObjects ? CVT_OBJECT : CVT_UNSUPPORTED; +// } +// +// /** +// * When called from a class static initializer, maps all native methods +// * found within that class to native libraries via the JNA raw calling +// * interface. Uses the class loader of the given class to search for the +// * native library in the resource path if it is not found in the system +// * library load path or jna.library.path. +// * @param cls Class with native methods to register +// * @param libName name of or path to native library to which functions +// * should be bound +// */ +// public static void register(Class cls, String libName) { +// NativeLibrary library = +// NativeLibrary.getInstance(libName, Collections.singletonMap(Library.OPTION_CLASSLOADER, cls.getClassLoader())); +// register(cls, library); +// } +// + /** + * When called from a class static initializer, maps all native methods found + * within that class to native libraries via the JNA raw calling interface. + * + * @param cls Class with native methods to register + * @param lib library to which functions should be bound + */ + // TODO: derive options from annotations (per-class or per-method) + // options: read parameter type mapping (long/native long), + // method name, library name, call conv + public static void register(Class cls, NativeLibrary lib) { + + /** + * Load the WASM module and re-interpret the native calls for JavaScript. + * The expectation is that the WASM developers and the JNA developers have + * coordinated their efforts to ensure that both are calling the same methods. + * @j2sNative + * + * Clazz._loadWasm(cls, lib); + * + */ +// { +// +// Method[] methods = cls.getDeclaredMethods(); +// List mlist = new ArrayList<>(); +// Map options = lib.getOptions(); +// TypeMapper mapper = (TypeMapper) options.get(Library.OPTION_TYPE_MAPPER); +// boolean allowObjects = Boolean.TRUE.equals(options.get(Library.OPTION_ALLOW_OBJECTS)); +// options = cacheOptions(cls, options, null); +// +// for (Method m : methods) { +// if ((m.getModifiers() & Modifier.NATIVE) != 0) { +// mlist.add(m); +// } +// } +// +// long[] handles = new long[mlist.size()]; +// for (int i = 0; i < handles.length; i++) { +// Method method = mlist.get(i); +// String sig = "("; +// Class rclass = method.getReturnType(); +// long rtype, closure_rtype; +// Class[] ptypes = method.getParameterTypes(); +// long[] atypes = new long[ptypes.length]; +// long[] closure_atypes = new long[ptypes.length]; +// int[] cvt = new int[ptypes.length]; +// ToNativeConverter[] toNative = new ToNativeConverter[ptypes.length]; +// FromNativeConverter fromNative = null; +// int rcvt = getConversion(rclass, mapper, allowObjects); +// boolean throwLastError = false; +// switch (rcvt) { +// case CVT_UNSUPPORTED: +// throw new IllegalArgumentException(rclass + " is not a supported return type (in method " +// + method.getName() + " in " + cls + ")"); +// case CVT_TYPE_MAPPER: +// case CVT_TYPE_MAPPER_STRING: +// case CVT_TYPE_MAPPER_WSTRING: +// fromNative = mapper.getFromNativeConverter(rclass); +// // FFIType.get() always looks up the native type for any given +// // class, so if we actually have conversion into a Java +// // object, make sure we use the proper type information +// closure_rtype = FFIType.get(rclass.isPrimitive() ? rclass : Pointer.class).getPointer().peer; +// rtype = FFIType.get(fromNative.nativeType()).getPointer().peer; +// break; +// case CVT_NATIVE_MAPPED: +// case CVT_NATIVE_MAPPED_STRING: +// case CVT_NATIVE_MAPPED_WSTRING: +// case CVT_INTEGER_TYPE: +// case CVT_POINTER_TYPE: +// closure_rtype = FFIType.get(Pointer.class).getPointer().peer; +// rtype = FFIType.get(NativeMappedConverter.getInstance(rclass).nativeType()).getPointer().peer; +// break; +// case CVT_STRUCTURE: +// case CVT_OBJECT: +// closure_rtype = rtype = FFIType.get(Pointer.class).getPointer().peer; +// break; +// case CVT_STRUCTURE_BYVAL: +// closure_rtype = FFIType.get(Pointer.class).getPointer().peer; +// rtype = FFIType.get(rclass).getPointer().peer; +// break; +// default: +// closure_rtype = rtype = FFIType.get(rclass).getPointer().peer; +// } +// +// for (int t = 0; t < ptypes.length; t++) { +// Class type = ptypes[t]; +// sig += getSignature(type); +// int conversionType = getConversion(type, mapper, allowObjects); +// cvt[t] = conversionType; +// if (conversionType == CVT_UNSUPPORTED) { +// throw new IllegalArgumentException(type + " is not a supported argument type (in method " +// + method.getName() + " in " + cls + ")"); +// } +// if ((conversionType == CVT_NATIVE_MAPPED) || (conversionType == CVT_NATIVE_MAPPED_STRING) +// || (conversionType == CVT_NATIVE_MAPPED_WSTRING) || (conversionType == CVT_INTEGER_TYPE)) { +// type = NativeMappedConverter.getInstance(type).nativeType(); +// } else if ((conversionType == CVT_TYPE_MAPPER) || (conversionType == CVT_TYPE_MAPPER_STRING) +// || (conversionType == CVT_TYPE_MAPPER_WSTRING)) { +// toNative[t] = mapper.getToNativeConverter(type); +// } +// +// // Determine the type that will be passed to the native +// // function, as well as the type to be passed +// // from Java initially +// switch (conversionType) { +// case CVT_STRUCTURE_BYVAL: +// case CVT_INTEGER_TYPE: +// case CVT_POINTER_TYPE: +// case CVT_NATIVE_MAPPED: +// case CVT_NATIVE_MAPPED_STRING: +// case CVT_NATIVE_MAPPED_WSTRING: +// atypes[t] = FFIType.get(type).getPointer().peer; +// closure_atypes[t] = FFIType.get(Pointer.class).getPointer().peer; +// break; +// case CVT_TYPE_MAPPER: +// case CVT_TYPE_MAPPER_STRING: +// case CVT_TYPE_MAPPER_WSTRING: +// closure_atypes[t] = FFIType.get(type.isPrimitive() ? type : Pointer.class).getPointer().peer; +// atypes[t] = FFIType.get(toNative[t].nativeType()).getPointer().peer; +// break; +// case CVT_DEFAULT: +// closure_atypes[t] = atypes[t] = FFIType.get(type).getPointer().peer; +// break; +// default: +// closure_atypes[t] = atypes[t] = FFIType.get(Pointer.class).getPointer().peer; +// } +// } +// sig += ")"; +// sig += getSignature(rclass); +// +// Class[] etypes = method.getExceptionTypes(); +// for (int e = 0; e < etypes.length; e++) { +// if (LastErrorException.class.isAssignableFrom(etypes[e])) { +// throwLastError = true; +// break; +// } +// } +// +// Function f = lib.getFunction(method.getName(), method); +// try { +// handles[i] = registerMethod(cls, method.getName(), sig, cvt, closure_atypes, atypes, rcvt, +// closure_rtype, rtype, method, f.peer, f.getCallingConvention(), throwLastError, toNative, +// fromNative, f.encoding); +// } catch (NoSuchMethodError e) { +// throw new UnsatisfiedLinkError( +// "No method " + method.getName() + " with signature " + sig + " in " + cls); +// } +// } +// synchronized (registeredClasses) { +// registeredClasses.put(cls, handles); +// registeredLibraries.put(cls, lib); +// } +// } + } +// +// /** +// * Get the {@link NativeLibrary} instance that is wrapped by the given +// * {@link Library} interface instance. +// * +// * @param library the {@link Library} interface instance, which was created +// * by the {@link Native#load Native.load()} method +// * @return the wrapped {@link NativeLibrary} instance +// */ +// public static NativeLibrary getNativeLibrary(final Library library) { +// if(library == null) { +// throw new IllegalArgumentException("null passed to getNativeLibrary"); +// } +// if(! Proxy.isProxyClass(library.getClass())) { +// throw new IllegalArgumentException("library object passed to getNativeLibrary in not a proxy"); +// } +// final InvocationHandler handler = Proxy.getInvocationHandler(library); +// if (!(handler instanceof Library.Handler)) { +// throw new IllegalArgumentException("Object is not a properly initialized Library interface instance"); +// } +// return ((Library.Handler) handler).getNativeLibrary(); +// } +// +// /** +// * Get the {@link NativeLibrary} instance to which the given "registered" +// * class is bound. +// * +// * @param cls the "registered" class, which was previously registered via +// * the {@link Native#register register()} method +// * @return the {@link NativeLibrary} instance to which the "registered" +// * class is bound +// */ +// public static NativeLibrary getNativeLibrary(final Class cls) { +// if(cls == null) { +// throw new IllegalArgumentException("null passed to getNativeLibrary"); +// } +// final Class mappedClass = findDirectMappedClass(cls); +// synchronized(registeredClasses) { +// final NativeLibrary nativeLibrary = registeredLibraries.get(mappedClass); +// if (nativeLibrary == null) { +// throw new IllegalArgumentException("Class " + cls.getName() + " is not currently registered"); +// } else { +// return nativeLibrary; +// } +// } +// } +// +// /* Take note of options used for a given library mapping, to facilitate +// * looking them up later. +// */ +// private static Map cacheOptions(Class cls, Map options, Object proxy) { +// Map libOptions = new HashMap<>(options); +// libOptions.put(_OPTION_ENCLOSING_LIBRARY, cls); +// typeOptions.put(cls, libOptions); +// if (proxy != null) { +// libraries.put(cls, new WeakReference<>(proxy)); +// } +// +// // If it's a direct mapping, AND implements a Library interface, +// // cache the library interface as well, so that any nested +// // classes get the appropriate associated options +// if (!cls.isInterface() +// && Library.class.isAssignableFrom(cls)) { +// Class ifaces[] = cls.getInterfaces(); +// for (Class ifc : ifaces) { +// if (Library.class.isAssignableFrom(ifc)) { +// cacheOptions(ifc, libOptions, proxy); +// break; +// } +// } +// } +// return libOptions; +// } +// +// private static native long registerMethod(Class cls, +// String name, +// String signature, +// int[] conversions, +// long[] closure_arg_types, +// long[] arg_types, +// int rconversion, +// long closure_rtype, +// long rtype, +// Method method, +// long fptr, +// int callingConvention, +// boolean throwLastError, +// ToNativeConverter[] toNative, +// FromNativeConverter fromNative, +// String encoding); +// +// +// // Called from native code +// private static NativeMapped fromNative(Class cls, Object value) { +// // NOTE: technically should be CallbackParameterContext +// return (NativeMapped)NativeMappedConverter.getInstance(cls).fromNative(value, new FromNativeContext(cls)); +// } +// // Called from native code +// private static NativeMapped fromNative(Method m, Object value) { +// Class cls = m.getReturnType(); +// return (NativeMapped)NativeMappedConverter.getInstance(cls).fromNative(value, new MethodResultContext(cls, null, null, m)); +// } +// // Called from native code +// private static Class nativeType(Class cls) { +// return NativeMappedConverter.getInstance(cls).nativeType(); +// } +// // Called from native code +// private static Object toNative(ToNativeConverter cvt, Object o) { +// // NOTE: technically should be either CallbackResultContext or +// // FunctionParameterContext +// return cvt.toNative(o, new ToNativeContext()); +// } +// // Called from native code +// private static Object fromNative(FromNativeConverter cvt, Object o, Method m) { +// return cvt.fromNative(o, new MethodResultContext(m.getReturnType(), null, null, m)); +// } +// +// /** Create a new cif structure. */ +// public static native long ffi_prep_cif(int abi, int nargs, long ffi_return_type, long ffi_types); +// /** Make an FFI function call. */ +// public static native void ffi_call(long cif, long fptr, long resp, long args); +// public static native long ffi_prep_closure(long cif, ffi_callback cb); +// public static native void ffi_free_closure(long closure); +// +// /** Returns the size (calculated by libffi) of the given type. */ +// static native int initialize_ffi_type(long type_info); +// +// public interface ffi_callback { +// void invoke(long cif, long resp, long argp); +// } +// +// /** Prints JNA library details to the console. */ +// public static void main(String[] args) { +// final String DEFAULT_TITLE = "Java Native Access (JNA)"; +// final String DEFAULT_VERSION = VERSION; +// final String DEFAULT_BUILD = VERSION + " (package information missing)"; +// Package pkg = Native.class.getPackage(); +// String title = pkg != null +// ? pkg.getSpecificationTitle() : DEFAULT_TITLE; +// if (title == null) title = DEFAULT_TITLE; +// String version = pkg != null +// ? pkg.getSpecificationVersion() : DEFAULT_VERSION; +// if (version == null) version = DEFAULT_VERSION; +// title += " API Version " + version; +// System.out.println(title); +// version = pkg != null +// ? pkg.getImplementationVersion() : DEFAULT_BUILD; +// if (version == null) version = DEFAULT_BUILD; +// System.out.println("Version: " + version); +// System.out.println(" Native: " + getNativeVersion() + " (" +// + getAPIChecksum() + ")"); +// System.out.println(" Prefix: " + Platform.RESOURCE_PREFIX); +// } +// +// /** Free the given callback trampoline. */ +// static synchronized native void freeNativeCallback(long ptr); +// +// /** Use direct mapping for callback. */ +// static final int CB_OPTION_DIRECT = 1; +// /** Return a DLL-resident fucntion pointer. */ +// static final int CB_OPTION_IN_DLL = 2; +// +// /** Create a native trampoline to delegate execution to the Java callback. +// */ +// static synchronized native long createNativeCallback(Callback callback, +// Method method, +// Class[] parameterTypes, +// Class returnType, +// int callingConvention, +// int flags, +// String encoding); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The value returned by the target native function +// */ +// static native int invokeInt(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The value returned by the target native function +// */ +// static native long invokeLong(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// */ +// static native void invokeVoid(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The value returned by the target native function +// */ +// static native float invokeFloat(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The value returned by the target native function +// */ +// static native double invokeDouble(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The value returned by the target native function +// */ +// static native long invokePointer(Function function, long fp, int callFlags, Object[] args); +// +// /** +// * Call the native function, returning a struct by value. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * @param memory Memory for pre-allocated structure to hold the result +// * @param typeInfo Native type information for the Structure +// */ +// private static native void invokeStructure(Function function, long fp, int callFlags, +// Object[] args, long memory, +// long type_info); +// +// /** +// * Call the native function, returning a struct by value. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return the passed-in Structure +// */ +// static Structure invokeStructure(Function function, long fp, int callFlags, Object[] args, +// Structure s) { +// invokeStructure(function, fp, callFlags, args, s.getPointer().peer, +// s.getTypeInfo().peer); +// return s; +// } +// +// /** +// * Call the native function, returning a Java Object. +// * +// * @param function Present to prevent the GC to collect the Function object +// * prematurely +// * @param fp function pointer +// * @param callFlags calling convention to be used +// * @param args Arguments to pass to the native function +// * +// * @return The returned Java Object +// */ +// static native Object invokeObject(Function function, long fp, int callFlags, Object[] args); +// +// /** Open the requested native library with default options. */ +// static long open(String name) { +// return open(name, -1); +// } +// +// /** Open the requested native library with the specified platform-specific +// * otions. +// */ +// static native long open(String name, int flags); +// +// /** Close the given native library. */ +// static native void close(long handle); +// +// static native long findSymbol(long handle, String name); +// +// /* +// ============================================================================ +// +// The first argument of the following read, write, get and set +// function is present to protect it from the GC. +// +// Although on the native side only the baseaddr and offset are used to access +// the memory, the Pointer argument must not be removed. This is the usecase: +// +// -------------------------------------- +// Memory pointer = ; +// +// String result = pointer.getWideString(0) +// +// -------------------------------------- +// +// In getWideString the pointer address is resolved and is passed to native. If +// the Memory object itself is not passed to native, the GC can collect the +// object at that point as it is not used anymore and the finalizers could run. +// +// The would introduce a race between the native call and the GC running the +// finalizers. The finalizers free the allocated memory, which results in +// a SEGFAULT. +// +// Passing only the Pointer object and loading the peer value via JNI was not +// implemented, as in microbenchmarks it showed large impact. Passing the +// Pointer object instead of the peer and offset value to getInt resulted in +// a performance of 70% of the unmodified source. +// +// ============================================================================ +// */ +// static native long indexOf(Pointer pointer, long baseaddr, long offset, byte value); +// +// static native void read(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, short[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, char[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, int[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, long[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, float[] buf, int index, int length); +// +// static native void read(Pointer pointer, long baseaddr, long offset, double[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, byte[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, short[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, char[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, int[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, long[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, float[] buf, int index, int length); +// +// static native void write(Pointer pointer, long baseaddr, long offset, double[] buf, int index, int length); +// +// static native byte getByte(Pointer pointer, long baseaddr, long offset); +// +// static native char getChar(Pointer pointer, long baseaddr, long offset); +// +// static native short getShort(Pointer pointer, long baseaddr, long offset); +// +// static native int getInt(Pointer pointer, long baseaddr, long offset); +// +// static native long getLong(Pointer pointer, long baseaddr, long offset); +// +// static native float getFloat(Pointer pointer, long baseaddr, long offset); +// +// static native double getDouble(Pointer pointer, long baseaddr, long offset); +// +// static Pointer getPointer(long addr) { +// long peer = _getPointer(addr); +// return peer == 0 ? null : new Pointer(peer); +// } +// +// private static native long _getPointer(long addr); +// +// static native String getWideString(Pointer pointer, long baseaddr, long offset); +// +// static String getString(Pointer pointer, long offset) { +// return getString(pointer, offset, getDefaultStringEncoding()); +// } +// +// static String getString(Pointer pointer, long offset, String encoding) { +// byte[] data = getStringBytes(pointer, pointer.peer, offset); +// if (encoding != null) { +// try { +// return new String(data, encoding); +// } +// catch(UnsupportedEncodingException e) { +// } +// } +// return new String(data); +// } +// +// static native byte[] getStringBytes(Pointer pointer, long baseaddr, long offset); +// +// static native void setMemory(Pointer pointer, long baseaddr, long offset, long length, byte value); +// +// static native void setByte(Pointer pointer, long baseaddr, long offset, byte value); +// +// static native void setShort(Pointer pointer, long baseaddr, long offset, short value); +// +// static native void setChar(Pointer pointer, long baseaddr, long offset, char value); +// +// static native void setInt(Pointer pointer, long baseaddr, long offset, int value); +// +// static native void setLong(Pointer pointer, long baseaddr, long offset, long value); +// +// static native void setFloat(Pointer pointer, long baseaddr, long offset, float value); +// +// static native void setDouble(Pointer pointer, long baseaddr, long offset, double value); +// +// static native void setPointer(Pointer pointer, long baseaddr, long offset, long value); +// +// static native void setWideString(Pointer pointer, long baseaddr, long offset, String value); +// +// static native ByteBuffer getDirectByteBuffer(Pointer pointer, long addr, long offset, long length); +// +// /** +// * Call the real native malloc +// * @param size size of the memory to be allocated +// * @return native address of the allocated memory block; zero if the +// * allocation failed. +// */ +// public static native long malloc(long size); +// +// /** +// * Call the real native free +// * @param ptr native address to be freed; a value of zero has no effect, +// * passing an already-freed pointer will cause pain. +// */ +// public static native void free(long ptr); +// +// private static final ThreadLocal nativeThreadTerminationFlag = +// new ThreadLocal() { +// @Override +// protected Memory initialValue() { +// Memory m = new Memory(4); +// m.clear(); +// return m; +// } +// }; +// private static final Map nativeThreads = Collections.synchronizedMap(new WeakHashMap()); +// +// /**

Indicate whether the JVM should detach the current native thread when +// the current Java code finishes execution. Generally this is used to +// avoid detaching native threads when it is known that a given thread +// will be relatively long-lived and call back to Java code frequently. +//

+// This call is lightweight; it only results in an additional JNI +// crossing if the desired state changes from its last setting. +// +// @throws IllegalStateException if {@link #detach detach(true)} is +// called on a thread created by the JVM. +// */ +// public static void detach(boolean detach) { +// Thread thread = Thread.currentThread(); +// if (detach) { +// // If a CallbackThreadInitializer was used to avoid detach, +// // we won't have put that thread into the nativeThreads map. +// // Performance is not as critical in that case, and since +// // detach is the default behavior, force an update of the detach +// // state every time. Clear the termination flag, since it's not +// // needed when the native thread is detached normally. +// nativeThreads.remove(thread); +// Pointer p = nativeThreadTerminationFlag.get(); +// setDetachState(true, 0); +// } +// else { +// if (!nativeThreads.containsKey(thread)) { +// Pointer p = nativeThreadTerminationFlag.get(); +// nativeThreads.put(thread, p); +// setDetachState(false, p.peer); +// } +// } +// } +// +// static Pointer getTerminationFlag(Thread t) { +// return nativeThreads.get(t); +// } +// +// private static native void setDetachState(boolean detach, long terminationFlag); +// +// private static class Buffers { +// static boolean isBuffer(Class cls) { +// return Buffer.class.isAssignableFrom(cls); +// } +// } +// +// /** Provides separation of JAWT functionality for the sake of J2ME +// * ports which do not include AWT support. +// */ +// private static class AWT { +// static long getWindowID(Window w) throws HeadlessException { +// return getComponentID(w); +// } +// // Declaring the argument as Object rather than Component avoids class not +// // found errors on phoneME foundation profile. +// static long getComponentID(Object o) throws HeadlessException { +// if (GraphicsEnvironment.isHeadless()) { +// throw new HeadlessException("No native windows when headless"); +// } +// Component c = (Component)o; +// if (c.isLightweight()) { +// throw new IllegalArgumentException("Component must be heavyweight"); +// } +// if (!c.isDisplayable()) +// throw new IllegalStateException("Component must be displayable"); +// // On X11 VMs prior to 1.5, the window must be visible +// if (Platform.isX11() +// && System.getProperty("java.version").startsWith("1.4")) { +// if (!c.isVisible()) { +// throw new IllegalStateException("Component must be visible"); +// } +// } +// // By this point, we're certain that Toolkit.loadLibraries() has +// // been called, thus avoiding AWT/JAWT link errors +// // (see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539705). +// return Native.getWindowHandle0(c); +// } +// } +} diff --git a/sources/net.sf.j2s.java.core/src/com/sun/jna/NativeLibrary.java b/sources/net.sf.j2s.java.core/src/com/sun/jna/NativeLibrary.java new file mode 100644 index 000000000..90e79e049 --- /dev/null +++ b/sources/net.sf.j2s.java.core/src/com/sun/jna/NativeLibrary.java @@ -0,0 +1,1086 @@ +/* Copyright (c) 2007 Wayne Meissner, All Rights Reserved + * Copyright (c) 2007-2013 Timothy Wall, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ + +package com.sun.jna; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +//import com.sun.jna.internal.Cleaner; +//import static com.sun.jna.Native.DEBUG_LOAD; +//import java.io.BufferedReader; +//import java.io.Closeable; +//import java.io.File; +//import java.io.FilenameFilter; +//import java.io.IOException; +//import java.io.InputStreamReader; +//import java.lang.ref.Reference; +//import java.lang.ref.WeakReference; +//import java.lang.reflect.InvocationTargetException; +//import java.lang.reflect.Method; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.Collection; +//import java.util.Collections; +//import java.util.HashMap; +//import java.util.HashSet; +//import java.util.LinkedHashSet; +//import java.util.LinkedList; +//import java.util.List; +//import java.util.Map; +//import java.util.Set; +//import java.util.StringTokenizer; +//import java.util.concurrent.ConcurrentHashMap; +//import java.util.logging.Level; +//import java.util.logging.Logger; + +/** + * Provides management of native library resources. One instance of this + * class corresponds to a single loaded native library. May also be used + * to map to the current process (see {@link NativeLibrary#getProcess()}). + *

+ * + * Library Search Paths + * A search for a given library will scan the following locations: + *

    + *
  1. jna.library.path User-customizable path + *
  2. jna.platform.library.path Platform-specific paths + *
  3. On OSX, ~/Library/Frameworks, + * /Library/Frameworks, and + * /System/Library/Frameworks will be searched for a framework + * with a name corresponding to that requested. Absolute paths to frameworks + * are also accepted, either ending at the framework name (sans ".framework") + * or the full path to the framework shared library + * (e.g. CoreServices.framework/CoreServices). + *
  4. Context class loader classpath. Deployed native libraries may be + * installed on the classpath under + * ${os-prefix}/LIBRARY_FILENAME, where ${os-prefix} + * is the OS/Arch prefix returned by {@link + * Platform#getNativeLibraryResourcePrefix()}. If bundled in a jar file, the + * resource will be extracted to jna.tmpdir for loading, and + * later removed (but only if jna.nounpack is false or not set). + *
+ * You may set the system property jna.debug_load=true to make + * JNA print the steps of its library search to the console. + * @author Wayne Meissner, split library loading from Function.java + * @author twall + */ +public class NativeLibrary {//implements Closeable { + +// private static final Logger LOG = Logger.getLogger(NativeLibrary.class.getName()); +// private static final Level DEBUG_LOAD_LEVEL = DEBUG_LOAD ? Level.INFO : Level.FINE; +// private static final SymbolProvider NATIVE_SYMBOL_PROVIDER = new SymbolProvider() { +// @Override +// public long getSymbolAddress(long handle, String name, SymbolProvider parent) { +// return Native.findSymbol(handle, name); +// } +// }; +// +// private final Cleaner.Cleanable cleanable; +// private volatile long handle; + private final String libraryName; +// private final String libraryPath; +// private final Map functions = new HashMap<>(); +// private final SymbolProvider symbolProvider; +// private final int callFlags; +// private final String encoding; +// private final Map options; + + private static final Map libraries = new HashMap<>(); +// +// private static final Map> searchPaths = new ConcurrentHashMap<>(); +// private static final LinkedHashSet librarySearchPath = new LinkedHashSet<>(); +// +// static { +// // Force initialization of native library +// if ((/** @j2sNative false && */Native.POINTER_SIZE == 0)) +// throw new Error("Native library not initialized"); +// } + +// private static String functionKey(String name, int flags, String encoding) { +// return name + "|" + flags + "|" + encoding; +// } + +// @SuppressWarnings("LeakingThisInConstructor") + private NativeLibrary(String libraryName, String libraryPath, long handle, Map options) { + this.libraryName = getLibraryName(libraryName); +// this.options = options; +// this.libraryPath = libraryPath; +// this.handle = handle; +// this.cleanable = Cleaner.getCleaner().register(this, new NativeLibraryDisposer(handle)); +// Object option = options.get(Library.OPTION_CALLING_CONVENTION); +// int callingConvention = option instanceof Number ? ((Number) option).intValue() : Function.C_CONVENTION; +// this.callFlags = callingConvention; +// SymbolProvider optionSymbolProvider = (SymbolProvider) options.get(Library.OPTION_SYMBOL_PROVIDER); +// if (optionSymbolProvider == null) { +// this.symbolProvider = NATIVE_SYMBOL_PROVIDER; +// } else { +// this.symbolProvider = optionSymbolProvider; +// } +// +// String encodingValue = (String) options.get(Library.OPTION_STRING_ENCODING); +// if (encodingValue == null) { +// encodingValue = Native.getDefaultStringEncoding(); +// } +// this.encoding = encodingValue; +// +// // Special workaround for w32 kernel32.GetLastError +// // Short-circuit the function to use built-in GetLastError access +// if (Platform.isWindows() && "kernel32".equals(this.libraryName.toLowerCase())) { +// synchronized (functions) { +// Function f = new Function(this, "GetLastError", Function.ALT_CONVENTION, encoding) { +// @Override +// Object invoke(Object[] args, Class returnType, boolean b, int fixedArgs) { +// return Integer.valueOf(Native.getLastError()); +// } +// +// @Override +// Object invoke(Method invokingMethod, Class[] paramTypes, Class returnType, +// Object[] inArgs, Map options) { +// return Integer.valueOf(Native.getLastError()); +// } +// }; +// functions.put(functionKey("GetLastError", callFlags, encoding), f); +// } +// } + } + +// private static final int DEFAULT_OPEN_OPTIONS = -1; +// private static int openFlags(Map options) { +// Object opt = options.get(Library.OPTION_OPEN_FLAGS); +// if (opt instanceof Number) { +// return ((Number)opt).intValue(); +// } +// return DEFAULT_OPEN_OPTIONS; +// } +// +// private static NativeLibrary loadLibrary(final String libraryName, final Map options) { +// LOG.log(DEBUG_LOAD_LEVEL, "Looking for library '" + libraryName + "'"); +// +// List exceptions = new ArrayList<>(); +// boolean isAbsolutePath = new File(libraryName).isAbsolute(); +// LinkedHashSet searchPath = new LinkedHashSet<>(); +// int openFlags = openFlags(options); +// +// // +// // Prepend any custom search paths specifically for this library +// // +// List customPaths = searchPaths.get(libraryName); +// if (customPaths != null) { +// synchronized (customPaths) { +// searchPath.addAll(customPaths); +// } +// } +// +// // Append web start path, if available. Note that this does not +// // attempt any library name variations +// String webstartPath = Native.getWebStartLibraryPath(libraryName); +// if (webstartPath != null) { +// LOG.log(DEBUG_LOAD_LEVEL, "Adding web start path " + webstartPath); +// searchPath.add(webstartPath); +// } +// +// LOG.log(DEBUG_LOAD_LEVEL, "Adding paths from jna.library.path: " + System.getProperty("jna.library.path")); +// +// searchPath.addAll(initPaths("jna.library.path")); +// String libraryPath = findLibraryPath(libraryName, searchPath); +// long handle = 0; +// // +// // Only search user specified paths first. This will also fall back +// // to dlopen/LoadLibrary() since findLibraryPath returns the mapped +// // name if it cannot find the library. +// // +// try { +// LOG.log(DEBUG_LOAD_LEVEL, "Trying " + libraryPath); +// handle = Native.open(libraryPath, openFlags); +// } catch(UnsatisfiedLinkError e) { +// // Add the system paths back for all fallback searching +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e.getMessage()); +// LOG.log(DEBUG_LOAD_LEVEL, "Adding system paths: " + librarySearchPath); +// exceptions.add(e); +// searchPath.addAll(librarySearchPath); +// } +// +// try { +// if (handle == 0) { +// libraryPath = findLibraryPath(libraryName, searchPath); +// LOG.log(DEBUG_LOAD_LEVEL, "Trying " + libraryPath); +// handle = Native.open(libraryPath, openFlags); +// if (handle == 0) { +// throw new UnsatisfiedLinkError("Failed to load library '" + libraryName + "'"); +// } +// } +// } catch(UnsatisfiedLinkError ule) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + ule.getMessage()); +// exceptions.add(ule); +// // For android, try to "preload" the library using +// // System.loadLibrary(), which looks into the private /data/data +// // path, not found in any properties +// if (Platform.isAndroid()) { +// try { +// LOG.log(DEBUG_LOAD_LEVEL, "Preload (via System.loadLibrary) " + libraryName); +// System.loadLibrary(libraryName); +// handle = Native.open(libraryPath, openFlags); +// } +// catch(UnsatisfiedLinkError e2) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e2.getMessage()); +// exceptions.add(e2); +// } +// } +// else if (Platform.isLinux() || Platform.isFreeBSD()) { +// // +// // Failed to load the library normally - try to match libfoo.so.* +// // +// LOG.log(DEBUG_LOAD_LEVEL, "Looking for version variants"); +// libraryPath = matchLibrary(libraryName, searchPath); +// if (libraryPath != null) { +// LOG.log(DEBUG_LOAD_LEVEL, "Trying " + libraryPath); +// try { +// handle = Native.open(libraryPath, openFlags); +// } +// catch(UnsatisfiedLinkError e2) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e2.getMessage()); +// exceptions.add(e2); +// } +// } +// } +// // Search framework libraries on OS X +// else if (Platform.isMac() && !libraryName.endsWith(".dylib")) { +// for(String frameworkName : matchFramework(libraryName)) { +// try { +// LOG.log(DEBUG_LOAD_LEVEL, "Trying " + frameworkName); +// handle = Native.open(frameworkName, openFlags); +// break; +// } +// catch(UnsatisfiedLinkError e2) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e2.getMessage()); +// exceptions.add(e2); +// } +// } +// } +// // Try the same library with a "lib" prefix +// else if (Platform.isWindows() && !isAbsolutePath) { +// LOG.log(DEBUG_LOAD_LEVEL, "Looking for lib- prefix"); +// libraryPath = findLibraryPath("lib" + libraryName, searchPath); +// if (libraryPath != null) { +// LOG.log(DEBUG_LOAD_LEVEL, "Trying " + libraryPath); +// try { +// handle = Native.open(libraryPath, openFlags); +// } catch(UnsatisfiedLinkError e2) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e2.getMessage()); +// exceptions.add(e2); +// } +// } +// } +// // As a last resort, try to extract the library from the class +// // path, using the current context class loader. +// if (handle == 0) { +// try { +// File embedded = Native.extractFromResourcePath(libraryName, (ClassLoader)options.get(Library.OPTION_CLASSLOADER)); +// if (embedded != null) { +// try { +// handle = Native.open(embedded.getAbsolutePath(), openFlags); +// libraryPath = embedded.getAbsolutePath(); +// } finally { +// // Don't leave temporary files around +// if (Native.isUnpacked(embedded)) { +// Native.deleteLibrary(embedded); +// } +// } +// } +// } +// catch(IOException e2) { +// LOG.log(DEBUG_LOAD_LEVEL, "Loading failed with message: " + e2.getMessage()); +// exceptions.add(e2); +// } +// } +// +// if (handle == 0) { +// StringBuilder sb = new StringBuilder(); +// sb.append("Unable to load library '"); +// sb.append(libraryName); +// sb.append("':"); +// for(Throwable t: exceptions) { +// sb.append("\n"); +// sb.append(t.getMessage()); +// } +// UnsatisfiedLinkError res = new UnsatisfiedLinkError(sb.toString()); +// for(Throwable t: exceptions) { +// addSuppressedReflected(res, t); +// } +// throw res; +// } +// } +// +// LOG.log(DEBUG_LOAD_LEVEL, "Found library '" + libraryName + "' at " + libraryPath); +// return new NativeLibrary(libraryName, libraryPath, handle, options); +// } +// +// private static Method addSuppressedMethod = null; +// static { +// try { +// addSuppressedMethod = Throwable.class.getMethod("addSuppressed", Throwable.class); +// } catch (NoSuchMethodException ex) { +// // This is the case for JDK < 7 +// } catch (SecurityException ex) { +// Logger.getLogger(NativeLibrary.class.getName()).log(Level.SEVERE, "Failed to initialize 'addSuppressed' method", ex); +// } +// } +// +// private static void addSuppressedReflected(Throwable target, Throwable suppressed) { +// if(addSuppressedMethod == null) { +// // Make this a NOOP on an unsupported JDK +// return; +// } +// try { +// addSuppressedMethod.invoke(target, suppressed); +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { +// throw new RuntimeException("Failed to call addSuppressedMethod", ex); +// } +// } +// +// /** Look for a matching framework (OSX) */ +// static String[] matchFramework(String libraryName) { +// Set paths = new LinkedHashSet<>(); +// File framework = new File(libraryName); +// if (framework.isAbsolute()) { +// if (libraryName.contains(".framework")) { +// if (framework.exists()) { +// return new String[]{framework.getAbsolutePath()}; +// } +// paths.add(framework.getAbsolutePath()); +// } +// else { +// framework = new File(new File(framework.getParentFile(), framework.getName() + ".framework"), framework.getName()); +// if (framework.exists()) { +// return new String[]{framework.getAbsolutePath()}; +// } +// paths.add(framework.getAbsolutePath()); +// } +// } +// else { +// final String[] PREFIXES = { System.getProperty("user.home"), "", "/System" }; +// String suffix = !libraryName.contains(".framework") +// ? libraryName + ".framework/" + libraryName : libraryName; +// for (String prefix : PREFIXES) { +// framework = new File(prefix + "/Library/Frameworks/" + suffix); +// if (framework.exists()) { +// return new String[]{framework.getAbsolutePath()}; +// } +// paths.add(framework.getAbsolutePath()); +// } +// } +// return paths.toArray(new String[0]); +// } +// + private String getLibraryName(String libraryName) { + return libraryName; +// String simplified = libraryName; +// final String BASE = "---"; +// String template = mapSharedLibraryName(BASE); +// int prefixEnd = template.indexOf(BASE); +// if (prefixEnd > 0 && simplified.startsWith(template.substring(0, prefixEnd))) { +// simplified = simplified.substring(prefixEnd); +// } +// String suffix = template.substring(prefixEnd + BASE.length()); +// int suffixStart = simplified.indexOf(suffix); +// if (suffixStart != -1) { +// simplified = simplified.substring(0, suffixStart); +// } +// return simplified; + } + + /** + * Returns an instance of NativeLibrary for the specified name. + * The library is loaded if not already loaded. If already loaded, the + * existing instance is returned.

+ * More than one name may map to the same NativeLibrary instance; only + * a single instance will be provided for any given unique file path. + * + * @param libraryName The library name to load. + * This can be short form (e.g. "c"), + * an explicit version (e.g. "libc.so.6"), or + * the full path to the library (e.g. "/lib/libc.so.6"). + */ + public static final NativeLibrary getInstance(String libraryName) { + return getInstance(libraryName, (Map) null);//Collections.emptyMap()); + } + + /** + * Returns an instance of NativeLibrary for the specified name. + * The library is loaded if not already loaded. If already loaded, the + * existing instance is returned.

+ * More than one name may map to the same NativeLibrary instance; only + * a single instance will be provided for any given unique file path. + * + * @param libraryName The library name to load. + * This can be short form (e.g. "c"), + * an explicit version (e.g. "libc.so.6"), or + * the full path to the library (e.g. "/lib/libc.so.6"). + * @param classLoader The class loader to use to load the native library. + * This only affects library loading when the native library is + * included somewhere in the classpath, either bundled in a jar file + * or as a plain file within the classpath. + */ + public static final NativeLibrary getInstance(String libraryName, ClassLoader classLoader) { + return getInstance(libraryName, (Map) null);//classLoader); + //Collections.singletonMap(Library.OPTION_CLASSLOADER, + // classLoader)); + } + + /** + * Returns an instance of NativeLibrary for the specified name. + * The library is loaded if not already loaded. If already loaded, the + * existing instance is returned.

+ * More than one name may map to the same NativeLibrary instance; only + * a single instance will be provided for any given unique file path. + * + * @param libraryName The library name to load. + * This can be short form (e.g. "c"), + * an explicit version (e.g. "libc.so.6" or + * "QuickTime.framework/Versions/Current/QuickTime"), or + * the full (absolute) path to the library (e.g. "/lib/libc.so.6"). + * @param libraryOptions Native library options for the given library (see {@link Library}). + */ + @SuppressWarnings("unused") + public static final NativeLibrary getInstance(String libraryName, Map libraryOptions) { +// Map options = new HashMap<>(libraryOptions); +// if (options.get(Library.OPTION_CALLING_CONVENTION) == null) { +// options.put(Library.OPTION_CALLING_CONVENTION, Integer.valueOf(Function.C_CONVENTION)); +// } +// +// // Use current process to load libraries we know are already +// // loaded by the VM to ensure we get the correct version +// if ((Platform.isLinux() || Platform.isFreeBSD() || Platform.isAIX()) +// && Platform.C_LIBRARY_NAME.equals(libraryName)) { +// libraryName = null; +// } + synchronized (libraries) { + NativeLibrary library = libraries.get(libraryName); +// Reference ref = libraries.get(libraryName);// + options); +// NativeLibrary library = (ref != null) ? ref.get() : null; + + if (library == null) { + library = new NativeLibrary(libraryName, null, 0, null); +// if (/** @j2sNative true || */false) { +// } else if (libraryName == null) { +// library = new NativeLibrary("", null, Native.open(null, openFlags(options)), options); +// } +// else { +// library = loadLibrary(libraryName, options); +// } +// ref = new WeakReference<>(library); + libraries.put(libraryName, library);// + options, ref); +// +// File file = library.getFile(); +// if (file != null) { +// libraries.put(file.getAbsolutePath() + options, ref); +// libraries.put(file.getName() + options, ref); +// } + } + return library; + } + } + + /** + * Returns an instance of NativeLibrary which refers to the current + * process. This is useful for accessing functions which were already + * mapped by some other mechanism, without having to reference or even + * know the exact name of the native library. + */ + public static synchronized final NativeLibrary getProcess() { + return getInstance(null); + } + + /** + * Returns an instance of NativeLibrary which refers to the current + * process. This is useful for accessing functions which were already + * mapped by some other mechanism, without having to reference or even + * know the exact name of the native library. + */ + public static synchronized final NativeLibrary getProcess(Map options) { + return getInstance(null, options); + } + +// /** +// * Add a path to search for the specified library, ahead of any system +// * paths. This is similar to setting jna.library.path, but +// * only extends the search path for a single library. +// * +// * @param libraryName The name of the library to use the path for +// * @param path The path to use when trying to load the library +// */ +// public static final void addSearchPath(String libraryName, String path) { +// List customPaths = searchPaths.get(libraryName); +// if (customPaths == null) { +// customPaths = Collections.synchronizedList(new ArrayList()); +// searchPaths.put(libraryName, customPaths); +// } +// +// customPaths.add(path); +// } +// +// /** +// * Create a new {@link Function} that is linked with a native +// * function that follows the NativeLibrary's calling convention. +// * +// *

The allocated instance represents a pointer to the named native +// * function from the library. +// * +// * @param functionName +// * Name of the native function to be linked with +// * @throws UnsatisfiedLinkError if the function is not found +// */ +// public Function getFunction(String functionName) { +// return getFunction(functionName, callFlags); +// } +// +// /** +// * Create a new {@link Function} that is linked with a native +// * function that follows the NativeLibrary's calling convention. +// * +// *

The allocated instance represents a pointer to the named native +// * function from the library. +// * +// * @param name +// * Name of the native function to be linked with. Uses a +// * function mapper option if one was provided to +// * transform the name. +// * @param method +// * Method to which the native function is to be mapped +// * @throws UnsatisfiedLinkError if the function is not found +// */ +// Function getFunction(String name, Method method) { +// FunctionMapper mapper = (FunctionMapper) options.get(Library.OPTION_FUNCTION_MAPPER); +// if (mapper != null) { +// name = mapper.getFunctionName(this, method); +// } +// // If there's native method profiler prefix, strip it +// String prefix = System.getProperty("jna.profiler.prefix", "$$YJP$$"); +// if (name.startsWith(prefix)) { +// name = name.substring(prefix.length()); +// } +// int flags = this.callFlags; +// Class[] etypes = method.getExceptionTypes(); +// for (int i=0;i < etypes.length;i++) { +// if (LastErrorException.class.isAssignableFrom(etypes[i])) { +// flags |= Function.THROW_LAST_ERROR; +// } +// } +// return getFunction(name, flags); +// } +// +// /** +// * Create a new {@link Function} that is linked with a native +// * function that follows a given calling flags. +// * +// * @param functionName +// * Name of the native function to be linked with +// * @param callFlags +// * Flags affecting the function invocation +// * @throws UnsatisfiedLinkError if the function is not found +// */ +// public Function getFunction(String functionName, int callFlags) { +// return getFunction(functionName, callFlags, encoding); +// } +// +// /** +// * Create a new {@link Function} that is linked with a native +// * function that follows a given calling flags. +// * +// * @param functionName +// * Name of the native function to be linked with +// * @param callFlags +// * Flags affecting the function invocation +// * @param encoding +// * Encoding to use to convert between Java and native +// * strings. +// * @throws UnsatisfiedLinkError if the function is not found +// */ +// public Function getFunction(String functionName, int callFlags, String encoding) { +// if (functionName == null) { +// throw new NullPointerException("Function name may not be null"); +// } +// synchronized (functions) { +// String key = functionKey(functionName, callFlags, encoding); +// Function function = functions.get(key); +// if (function == null) { +// function = new Function(this, functionName, callFlags, encoding); +// functions.put(key, function); +// } +// return function; +// } +// } +// +// /** @return this native library instance's options. */ +// public Map getOptions() { +// return options; +// } +// +// /** Look up the given global variable within this library. +// * @param symbolName +// * @return Pointer representing the global variable address +// * @throws UnsatisfiedLinkError if the symbol is not found +// */ +// public Pointer getGlobalVariableAddress(String symbolName) { +// try { +// return new Pointer(getSymbolAddress(symbolName)); +// } catch(UnsatisfiedLinkError e) { +// throw new UnsatisfiedLinkError("Error looking up '" + symbolName + "': " + e.getMessage()); +// } +// } +// +// /** +// * Used by the Function class to locate a symbol +// * @throws UnsatisfiedLinkError if the symbol can't be found +// */ +// long getSymbolAddress(String name) { +// if (handle == 0) { +// throw new UnsatisfiedLinkError("Library has been unloaded"); +// } +// return this.symbolProvider.getSymbolAddress(handle, name, NATIVE_SYMBOL_PROVIDER); +// } +// +// @Override +// public String toString() { +// return "Native Library <" + libraryPath + "@" + handle + ">"; +// } + /** Returns the simple name of this library. */ + public String getName() { + return libraryName; + } +// /** +// * Returns the file on disk corresponding to this NativeLibrary instance. +// * If this NativeLibrary represents the current process, this function will return null. +// */ +// public File getFile() { +// if (libraryPath == null) +// return null; +// return new File(libraryPath); +// } +// +// /** Close all open native libraries. */ +// static void disposeAll() { +// Set> values; +// synchronized(libraries) { +// values = new LinkedHashSet<>(libraries.values()); +// } +// for (Reference ref : values) { +// NativeLibrary lib = ref.get(); +// if (lib != null) { +// lib.close(); +// } +// } +// } +// +// /** Close the native library we're mapped to. */ +// public void close() { +// Set keys = new HashSet<>(); +// synchronized(libraries) { +// for (Map.Entry> e : libraries.entrySet()) { +// Reference ref = e.getValue(); +// if (ref.get() == this) { +// keys.add(e.getKey()); +// } +// } +// +// for (String k : keys) { +// libraries.remove(k); +// } +// } +// +// synchronized(this) { +// if (handle != 0) { +// handle = 0; +// cleanable.clean(); +// } +// } +// } +// +// @Deprecated +// public void dispose() { +// close(); +// } +// +// private static List initPaths(String key) { +// String value = System.getProperty(key, ""); +// if ("".equals(value)) { +// return Collections.emptyList(); +// } +// StringTokenizer st = new StringTokenizer(value, File.pathSeparator); +// List list = new ArrayList<>(); +// while (st.hasMoreTokens()) { +// String path = st.nextToken(); +// if (!"".equals(path)) { +// list.add(path); +// } +// } +// return list; +// } +// +// /** Use standard library search paths to find the library. */ +// private static String findLibraryPath(String libName, Collection searchPath) { +// +// // +// // If a full path to the library was specified, don't search for it +// // +// if (new File(libName).isAbsolute()) { +// return libName; +// } +// +// // +// // Get the system name for the library (e.g. libfoo.so) +// // +// String name = mapSharedLibraryName(libName); +// +// // Search in the JNA paths for it +// for (String path : searchPath) { +// File file = new File(path, name); +// if (file.exists()) { +// return file.getAbsolutePath(); +// } +// if (Platform.isMac()) { +// // Native libraries delivered via JNLP class loader +// // may require a .jnilib extension to be found +// if (name.endsWith(".dylib")) { +// file = new File(path, name.substring(0, name.lastIndexOf(".dylib")) + ".jnilib"); +// if (file.exists()) { +// return file.getAbsolutePath(); +// } +// } +// } +// } +// +// // +// // Default to returning the mapped library name and letting the system +// // search for it +// // +// return name; +// } +// +// /** Similar to {@link System#mapLibraryName}, except that it maps to +// standard shared library formats rather than specifically JNI formats. +// @param libName base (undecorated) name of library +// */ +// static String mapSharedLibraryName(String libName) { +// if (Platform.isMac()) { +// if (libName.startsWith("lib") +// && (libName.endsWith(".dylib") +// || libName.endsWith(".jnilib"))) { +// return libName; +// } +// String name = System.mapLibraryName(libName); +// // On MacOSX, System.mapLibraryName() returns the .jnilib extension +// // (the suffix for JNI libraries); ordinarily shared libraries have +// // a .dylib suffix +// if (name.endsWith(".jnilib")) { +// return name.substring(0, name.lastIndexOf(".jnilib")) + ".dylib"; +// } +// return name; +// } +// else if (Platform.isLinux() || Platform.isFreeBSD()) { +// if (isVersionedName(libName) || libName.endsWith(".so")) { +// // A specific version was requested - use as is for search +// return libName; +// } +// } +// else if (Platform.isAIX()) { // can be libx.a, libx.a(shr.o), libx.so +// if (isVersionedName(libName) || libName.endsWith(".so") || libName.startsWith("lib") || libName.endsWith(".a")) { +// // A specific version was requested - use as is for search +// return libName; +// } +// } +// else if (Platform.isWindows()) { +// if (libName.endsWith(".drv") || libName.endsWith(".dll") || libName.endsWith(".ocx")) { +// return libName; +// } +// } +// +// String mappedName = System.mapLibraryName(libName); +// if(Platform.isAIX() && mappedName.endsWith(".so")) { +// return mappedName.replaceAll(".so$", ".a"); +// } else { +// return mappedName; +// } +// } +// +// private static boolean isVersionedName(String name) { +// if (name.startsWith("lib")) { +// int so = name.lastIndexOf(".so."); +// if (so != -1 && so + 4 < name.length()) { +// for (int i=so+4;i < name.length();i++) { +// char ch = name.charAt(i); +// if (!Character.isDigit(ch) && ch != '.') { +// return false; +// } +// } +// return true; +// } +// } +// return false; +// } +// +// /** +// * matchLibrary() is very Linux specific. It is here to deal with the case +// * where /usr/lib/libc.so does not exist, or it is not a valid symlink to +// * a versioned file (e.g. /lib/libc.so.6). +// */ +// static String matchLibrary(final String libName, Collection searchPath) { +// File lib = new File(libName); +// if (lib.isAbsolute()) { +// searchPath = Arrays.asList(lib.getParent()); +// } +// FilenameFilter filter = new FilenameFilter() { +// @Override +// public boolean accept(File dir, String filename) { +// return (filename.startsWith("lib" + libName + ".so") +// || (filename.startsWith(libName + ".so") +// && libName.startsWith("lib"))) +// && isVersionedName(filename); +// } +// }; +// +// Collection matches = new LinkedList<>(); +// for (String path : searchPath) { +// File[] files = new File(path).listFiles(filter); +// if (files != null && files.length > 0) { +// matches.addAll(Arrays.asList(files)); +// } +// } +// +// // +// // Search through the results and return the highest numbered version +// // i.e. libc.so.6 is preferred over libc.so.5 +// double bestVersion = -1; +// String bestMatch = null; +// for (File f : matches) { +// String path = f.getAbsolutePath(); +// String ver = path.substring(path.lastIndexOf(".so.") + 4); +// double version = parseVersion(ver); +// if (version > bestVersion) { +// bestVersion = version; +// bestMatch = path; +// } +// } +// return bestMatch; +// } +// +// static double parseVersion(String ver) { +// double v = 0; +// double divisor = 1; +// int dot = ver.indexOf("."); +// while (ver != null) { +// String num; +// if (dot != -1) { +// num = ver.substring(0, dot); +// ver = ver.substring(dot + 1); +// dot = ver.indexOf("."); +// } +// else { +// num = ver; +// ver = null; +// } +// try { +// v += Integer.parseInt(num) / divisor; +// } +// catch(NumberFormatException e) { +// return 0; +// } +// divisor *= 100; +// } +// +// return v; +// } +// +// static { +// String webstartPath = Native.getWebStartLibraryPath("jnidispatch"); +// if (webstartPath != null) { +// librarySearchPath.add(webstartPath); +// } +// if (System.getProperty("jna.platform.library.path") == null +// && !Platform.isWindows()) { +// // Add default path lookups for unix-like systems +// String platformPath = ""; +// String sep = ""; +// String archPath = ""; +// +// // +// // Search first for an arch specific path if one exists, but always +// // include the generic paths if they exist. +// // NOTES (wmeissner): +// // Some older linux amd64 distros did not have /usr/lib64, and +// // 32bit distros only have /usr/lib. FreeBSD also only has +// // /usr/lib by default, with /usr/lib32 for 32bit compat. +// // Solaris seems to have both, but defaults to 32bit userland even +// // on 64bit machines, so we have to explicitly search the 64bit +// // one when running a 64bit JVM. +// // +// if (Platform.isLinux() || Platform.isSolaris() +// || Platform.isFreeBSD() || Platform.iskFreeBSD()) { +// // Linux & FreeBSD use /usr/lib32, solaris uses /usr/lib/32 +// archPath = (Platform.isSolaris() ? "/" : "") + Native.POINTER_SIZE * 8; +// } +// String[] paths = { +// "/usr/lib" + archPath, +// "/lib" + archPath, +// "/usr/lib", +// "/lib", +// }; +// // Multi-arch support on Ubuntu (and other +// // multi-arch distributions) +// // paths is scanned against real directory +// // so for platforms which are not multi-arch +// // this should continue to work. +// if (Platform.isLinux() || Platform.iskFreeBSD() || Platform.isGNU()) { +// String multiArchPath = getMultiArchPath(); +// +// // Assemble path with all possible options +// paths = new String[] { +// "/usr/lib/" + multiArchPath, +// "/lib/" + multiArchPath, +// "/usr/lib" + archPath, +// "/lib" + archPath, +// "/usr/lib", +// "/lib", +// }; +// } +// +// // We might be wrong with the multiArchPath above. Raspbian, +// // the Raspberry Pi flavor of Debian, for example, uses +// // uses arm-linux-gnuabihf since it's using the hard-float +// // ABI for armv6. Other distributions might use a different +// // tuple for the same thing. Query ldconfig to get the additional +// // library paths it knows about. +// if (Platform.isLinux()) { +// ArrayList ldPaths = getLinuxLdPaths(); +// // prepend the paths we already have +// for (int i=paths.length-1; 0 <= i; i--) { +// int found = ldPaths.indexOf(paths[i]); +// if (found != -1) { +// ldPaths.remove(found); +// } +// ldPaths.add(0, paths[i]); +// } +// paths = ldPaths.toArray(new String[0]); +// } +// +// for (int i=0;i < paths.length;i++) { +// File dir = new File(paths[i]); +// if (dir.exists() && dir.isDirectory()) { +// platformPath += sep + paths[i]; +// sep = File.pathSeparator; +// } +// } +// if (!"".equals(platformPath)) { +// System.setProperty("jna.platform.library.path", platformPath); +// } +// } +// librarySearchPath.addAll(initPaths("jna.platform.library.path")); +// } +// +// private static String getMultiArchPath() { +// String cpu = Platform.ARCH; +// String kernel = Platform.iskFreeBSD() +// ? "-kfreebsd" +// : (Platform.isGNU() ? "" : "-linux"); +// String libc = "-gnu"; +// +// if (Platform.isIntel()) { +// cpu = (Platform.is64Bit() ? "x86_64" : "i386"); +// } +// else if (Platform.isPPC()) { +// cpu = (Platform.is64Bit() ? "powerpc64" : "powerpc"); +// } +// else if (Platform.isARM()) { +// cpu = "arm"; +// libc = "-gnueabi"; +// } +// else if (Platform.ARCH.equals("mips64el")) { +// libc = "-gnuabi64"; +// } +// +// return cpu + kernel + libc; +// } +// +// /** +// * Get the library paths from ldconfig cache. Tested against ldconfig 2.13. +// */ +// private static ArrayList getLinuxLdPaths() { +// ArrayList ldPaths = new ArrayList<>(); +// Process process = null; +// BufferedReader reader = null; +// try { +// process = Runtime.getRuntime().exec("/sbin/ldconfig -p"); +// reader = new BufferedReader(new InputStreamReader(process.getInputStream())); +// String buffer; +// while ((buffer = reader.readLine()) != null) { +// int startPath = buffer.indexOf(" => "); +// int endPath = buffer.lastIndexOf('/'); +// if (startPath != -1 && endPath != -1 && startPath < endPath) { +// String path = buffer.substring(startPath + 4, endPath); +// if (!ldPaths.contains(path)) { +// ldPaths.add(path); +// } +// } +// } +// } catch (Exception e) { +// } finally { +// if(reader != null) { +// try { +// reader.close(); +// } catch (IOException e) { +// } +// } +// if(process != null) { +// try { +// process.waitFor(); +// } catch (InterruptedException e) { +// } +// } +// } +// return ldPaths; +// } +// +// private static final class NativeLibraryDisposer implements Runnable { +// +// private long handle; +// +// public NativeLibraryDisposer(long handle) { +// this.handle = handle; +// } +// +// public synchronized void run() { +// if (handle != 0) { +// try { +// Native.close(handle); +// } finally { +// handle = 0; +// } +// } +// } +// +// } +} diff --git a/sources/net.sf.j2s.java.core/src/com/sun/jna/Pointer.java b/sources/net.sf.j2s.java.core/src/com/sun/jna/Pointer.java new file mode 100644 index 000000000..4a09e2336 --- /dev/null +++ b/sources/net.sf.j2s.java.core/src/com/sun/jna/Pointer.java @@ -0,0 +1,1371 @@ +/* + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna; + +//import java.io.PrintWriter; +//import java.io.StringWriter; +//import java.lang.reflect.Array; +//import java.nio.Buffer; +//import java.nio.ByteBuffer; +//import java.nio.ByteOrder; +//import java.util.ArrayList; +//import java.util.List; +// +///** +// * An abstraction for a native pointer data type. A Pointer instance +// * represents, on the Java side, a native pointer. The native pointer could +// * be any type of native pointer. Methods such as write, +// * read, getXXX, and setXXX, provide +// * means to access memory underlying the native pointer.

+// * While a constructor exists to create a Pointer from an integer value, it's +// * not generally a good idea to be creating pointers that way. +// * +// * @author Sheng Liang, originator +// * @author Todd Fast, suitability modifications +// * @author Timothy Wall, robust library loading +// * @see Function +// */ +public class Pointer { + + /** Convenience constant, same as null. */ + public static final Pointer NULL = null; + + /** Convenience constant, equivalent to (void*)CONSTANT. */ + public static final Pointer createConstant(long peer) { + return new Pointer((int) peer); + } + + public static final Pointer createConstant(int peer) { + return new Pointer(peer); + } + + /** Pointer value of the real native pointer. Use long to be 64-bit safe. + */ + protected int peer; + + /** Derived class must assign peer pointer value. */ + Pointer() { + super(); + } + + /** Create from native pointer. Don't use this unless you know what + * you're doing. + */ + public Pointer(int peer) { + this.peer = peer; + } + +// /** Provide a view of this memory using the given offset to calculate a new base address. */ +// public Pointer share(long offset) { +// return share(offset, 0); +// } +// +// /** Provide a view of this memory using the given offset to calculate a +// * new base address, bounds-limiting the memory with the given size. +// */ +// public Pointer share(long offset, long sz) { +// if (offset == 0L) { +// return this; +// } +// return new Pointer(peer + offset); +// } +// +// /** Zero memory for the given number of bytes. */ +// public void clear(long size) { +// setMemory(0, size, (byte)0); +// } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null) { + return false; + } + return (o instanceof Pointer) && (((Pointer)o).peer == peer); + } + + @Override + public int hashCode() { + return peer;//(int) ((peer >>> 32) + (peer & 0xFFFFFFFFL)); + } + + +// ////////////////////////////////////////////////////////////////////////// +// // Raw read methods +// ////////////////////////////////////////////////////////////////////////// +// +// /** Returns the offset of the given value in memory from the given offset, +// * or -1 if the value is not found. +// */ +// public long indexOf(long offset, byte value) { +// return Native.indexOf(this, this.peer, offset, value); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf byte array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, byte[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf short array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, short[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf char array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, char[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf int array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, int[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf long array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, long[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf float array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, float[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf double array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, double[] buf, int index, int length) { +// Native.read(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying from memory pointed to by +// * native pointer, into the specified array. +// * +// * @param offset byte offset from pointer from which data is copied +// * @param buf {@link Pointer} array into which data is copied +// * @param index array index to which data is copied +// * @param length number of elements from native pointer that must be copied +// */ +// public void read(long offset, Pointer[] buf, int index, int length) { +// for (int i=0;i < length;i++) { +// Pointer p = getPointer(offset + i*Native.POINTER_SIZE); +// Pointer oldp = buf[i+index]; +// // Avoid replacing the original pointer if it hasn't changed +// if (oldp == null || p == null || p.peer != oldp.peer) { +// buf[i+index] = p; +// } +// } +// } +// +// +// ////////////////////////////////////////////////////////////////////////// +// // Raw write methods +// ////////////////////////////////////////////////////////////////////////// +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf byte array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, byte[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf short array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, short[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf char array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, char[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf int array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, int[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf long array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, long[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf float array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, float[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** +// * Indirect the native pointer, copying into memory pointed to by +// * native pointer, from the specified array. +// * +// * @param offset byte offset from pointer into which data is copied +// * @param buf double array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long offset, double[] buf, int index, int length) { +// Native.write(this, this.peer, offset, buf, index, length); +// } +// +// /** Write the given array of Pointer to native memory. +// * @param bOff byte offset from pointer into which data is copied +// * @param buf Pointer array from which to copy +// * @param index array index from which to start copying +// * @param length number of elements from buf that must be +// * copied +// */ +// public void write(long bOff, Pointer[] buf, int index, int length) { +// for (int i=0;i < length;i++) { +// setPointer(bOff + i * Native.POINTER_SIZE, buf[index + i]); +// } +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Java type read methods +// ////////////////////////////////////////////////////////////////////////// +// +// Object getValue(long offset, Class type, Object currentValue) { +// +// Object result = null; +// if (Structure.class.isAssignableFrom(type)) { +// Structure s = (Structure)currentValue; +// if (Structure.ByReference.class.isAssignableFrom(type)) { +// s = Structure.updateStructureByReference((Class) type, s, getPointer(offset)); +// } else { +// s.useMemory(this, (int)offset, true); +// s.read(); +// } +// result = s; +// } else if (type == boolean.class || type == Boolean.class) { +// result = Function.valueOf(getInt(offset) != 0); +// } else if (type == byte.class || type == Byte.class) { +// result = Byte.valueOf(getByte(offset)); +// } else if (type == short.class || type == Short.class) { +// result = Short.valueOf(getShort(offset)); +// } else if (type == char.class || type == Character.class) { +// result = Character.valueOf(getChar(offset)); +// } else if (type == int.class || type == Integer.class) { +// result = Integer.valueOf(getInt(offset)); +// } else if (type == long.class || type == Long.class) { +// result = Long.valueOf(getLong(offset)); +// } else if (type == float.class || type == Float.class) { +// result = Float.valueOf(getFloat(offset)); +// } else if (type == double.class || type == Double.class) { +// result = Double.valueOf(getDouble(offset)); +// } else if (Pointer.class.isAssignableFrom(type)) { +// Pointer p = getPointer(offset); +// if (p != null) { +// Pointer oldp = currentValue instanceof Pointer +// ? (Pointer)currentValue : null; +// if (oldp == null || p.peer != oldp.peer) { +// result = p; +// } else { +// result = oldp; +// } +// } +// } else if (type == String.class) { +// Pointer p = getPointer(offset); +// result = p != null ? p.getString(0) : null; +// } else if (type == WString.class) { +// Pointer p = getPointer(offset); +// result = p != null ? new WString(p.getWideString(0)) : null; +// } else if (Callback.class.isAssignableFrom(type)) { +// // Overwrite the Java memory if the native pointer is a different +// // function pointer. +// Pointer fp = getPointer(offset); +// if (fp == null) { +// result = null; +// } else { +// Callback cb = (Callback)currentValue; +// Pointer oldfp = CallbackReference.getFunctionPointer(cb); +// if (!fp.equals(oldfp)) { +// cb = CallbackReference.getCallback(type, fp); +// } +// result = cb; +// } +// } else if (Platform.HAS_BUFFERS && Buffer.class.isAssignableFrom(type)) { +// Pointer bp = getPointer(offset); +// if (bp == null) { +// result = null; +// } else { +// Pointer oldbp = currentValue == null ? null +// : Native.getDirectBufferPointer((Buffer)currentValue); +// if (oldbp == null || !oldbp.equals(bp)) { +// throw new IllegalStateException("Can't autogenerate a direct buffer on memory read"); +// } +// result = currentValue; +// } +// } else if (NativeMapped.class.isAssignableFrom(type)) { +// NativeMapped nm = (NativeMapped)currentValue; +// if (nm != null) { +// Object value = getValue(offset, nm.nativeType(), null); +// result = nm.fromNative(value, new FromNativeContext(type)); +// if (nm.equals(result)) { +// result = nm; +// } +// } else { +// NativeMappedConverter tc = NativeMappedConverter.getInstance(type); +// Object value = getValue(offset, tc.nativeType(), null); +// result = tc.fromNative(value, new FromNativeContext(type)); +// } +// } else if (type.isArray()) { +// result = currentValue; +// if (result == null) { +// throw new IllegalStateException("Need an initialized array"); +// } +// readArray(offset, result, type.getComponentType()); +// } else { +// throw new IllegalArgumentException("Reading \"" + type + "\" from memory is not supported"); +// } +// return result; +// } +// +// /** Read memory starting at offset into the array with element type cls. */ +// private void readArray(long offset, Object o, Class cls) { +// int length = 0; +// length = Array.getLength(o); +// Object result = o; +// +// if (cls == byte.class) { +// read(offset, (byte[])result, 0, length); +// } +// else if (cls == short.class) { +// read(offset, (short[])result, 0, length); +// } +// else if (cls == char.class) { +// read(offset, (char[])result, 0, length); +// } +// else if (cls == int.class) { +// read(offset, (int[])result, 0, length); +// } +// else if (cls == long.class) { +// read(offset, (long[])result, 0, length); +// } +// else if (cls == float.class) { +// read(offset, (float[])result, 0, length); +// } +// else if (cls == double.class) { +// read(offset, (double[])result, 0, length); +// } +// else if (Pointer.class.isAssignableFrom(cls)) { +// read(offset, (Pointer[])result, 0, length); +// } +// else if (Structure.class.isAssignableFrom(cls)) { +// Structure[] sarray = (Structure[])result; +// if (Structure.ByReference.class.isAssignableFrom(cls)) { +// Pointer[] parray = getPointerArray(offset, sarray.length); +// for (int i=0;i < sarray.length;i++) { +// sarray[i] = Structure.updateStructureByReference((Class) cls, sarray[i], parray[i]); +// } +// } +// else { +// Structure first = sarray[0]; +// if (first == null) { +// first = Structure.newInstance((Class) cls, share(offset)); +// first.conditionalAutoRead(); +// sarray[0] = first; +// } +// else { +// first.useMemory(this, (int)offset, true); +// first.read(); +// } +// Structure[] tmp = first.toArray(sarray.length); +// for (int i=1;i < sarray.length;i++) { +// if (sarray[i] == null) { +// // Structure.toArray() takes care of read +// sarray[i] = tmp[i]; +// } +// else { +// sarray[i].useMemory(this, (int)(offset + i * sarray[i].size()), true); +// sarray[i].read(); +// } +// } +// } +// } +// else if (NativeMapped.class.isAssignableFrom(cls)) { +// NativeMapped[] array = (NativeMapped[])result; +// NativeMappedConverter tc = NativeMappedConverter.getInstance(cls); +// int size = Native.getNativeSize(result.getClass(), result) / array.length; +// for (int i=0;i < array.length;i++) { +// Object value = getValue(offset + size*i, tc.nativeType(), array[i]); +// array[i] = (NativeMapped)tc.fromNative(value, new FromNativeContext(cls)); +// } +// } +// else { +// throw new IllegalArgumentException("Reading array of " +// + cls +// + " from memory not supported"); +// } +// } +// +// /** +// * Indirect the native pointer as a pointer to byte. This is +// * equivalent to the expression +// * *((jbyte *)((char *)Pointer + offset)). +// * +// * @param offset offset from pointer to perform the indirection +// * @return the byte value being pointed to +// */ +// public byte getByte(long offset) { +// return Native.getByte(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to wchar_t. This +// * is equivalent to the expression +// * *((wchar_t*)((char *)Pointer + offset)). +// * +// * @param offset offset from pointer to perform the indirection +// * @return the wchar_t value being pointed to +// */ +// public char getChar(long offset) { +// return Native.getChar(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to short. This is +// * equivalent to the expression +// * *((jshort *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the short value being pointed to +// */ +// public short getShort(long offset) { +// return Native.getShort(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to int. This is +// * equivalent to the expression +// * *((jint *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the int value being pointed to +// */ +// public int getInt(long offset) { +// return Native.getInt(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to long. This is +// * equivalent to the expression +// * *((jlong *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the long value being pointed to +// */ +// public long getLong(long offset) { +// return Native.getLong(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to long. This is +// * equivalent to the expression +// * *((long *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the long value being pointed to +// */ +// public NativeLong getNativeLong(long offset) { +// return new NativeLong(NativeLong.SIZE == 8 ? getLong(offset) : getInt(offset)); +// } +// +// /** +// * Indirect the native pointer as a pointer to float. This is +// * equivalent to the expression +// * *((jfloat *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the float value being pointed to +// */ +// public float getFloat(long offset) { +// return Native.getFloat(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to double. This +// * is equivalent to the expression +// * *((jdouble *)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return the double value being pointed to +// */ +// public double getDouble(long offset) { +// return Native.getDouble(this, this.peer, offset); +// } +// +// /** +// * Indirect the native pointer as a pointer to pointer. This is equivalent +// * to the expression +// * *((void **)((char *)Pointer + offset)). +// * +// * @param offset byte offset from pointer to perform the indirection +// * @return a {@link Pointer} equivalent of the pointer value +// * being pointed to, or null if the pointer value is +// * NULL; +// */ +// public Pointer getPointer(long offset) { +// return Native.getPointer(peer + offset); +// } +// +// /** +// * Get a ByteBuffer mapped to the memory pointed to by the pointer, +// * ensuring the buffer uses native byte order. +// * +// * @param offset byte offset from pointer to start the buffer +// * @param length Length of ByteBuffer +// * @return a direct ByteBuffer that accesses the memory being pointed to, +// */ +// public ByteBuffer getByteBuffer(long offset, long length) { +// return Native.getDirectByteBuffer(this, this.peer, offset, length).order(ByteOrder.nativeOrder()); +// } +// +// /** Read a wide (const wchar_t *) string from memory. */ +// public String getWideString(long offset) { +// return Native.getWideString(this, this.peer, offset); +// } +// +// /** +// * Copy native memory to a Java String. The encoding used is obtained +// * form {@link Native#getDefaultStringEncoding()}. +// * +// * @param offset byte offset from pointer to start reading bytes +// * @return the String value being pointed to +// */ +// public String getString(long offset) { +// return getString(offset, Native.getDefaultStringEncoding()); +// } +// +// /** +// * Copy native memory to a Java String using the requested encoding. +// * +// * @param offset byte offset from pointer to obtain the native string +// * @param encoding the desired encoding +// * @return the String value being pointed to +// */ +// public String getString(long offset, String encoding) { +// return Native.getString(this, offset, encoding); +// } +// +// /** Read a native array of bytes of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public byte[] getByteArray(long offset, int arraySize) { +// byte[] buf = new byte[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of wchar_t of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public char[] getCharArray(long offset, int arraySize) { +// char[] buf = new char[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of int16 of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public short[] getShortArray(long offset, int arraySize) { +// short[] buf = new short[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of int32 of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public int[] getIntArray(long offset, int arraySize) { +// int[] buf = new int[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of int64 of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public long[] getLongArray(long offset, int arraySize) { +// long[] buf = new long[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of float of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public float[] getFloatArray(long offset, int arraySize) { +// float[] buf = new float[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Read a native array of double of size arraySize from the +// given offset from this {@link Pointer}. +// */ +// public double[] getDoubleArray(long offset, int arraySize) { +// double[] buf = new double[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /** Returns an array of {@link Pointer}. The array length is +// * determined by a NULL-valued terminating element. +// */ +// public Pointer[] getPointerArray(long offset) { +// List array = new ArrayList<>(); +// int addOffset = 0; +// Pointer p = getPointer(offset); +// while (p != null) { +// array.add(p); +// addOffset += Native.POINTER_SIZE; +// p = getPointer(offset + addOffset); +// } +// return array.toArray(new Pointer[0]); +// } +// +// /** Returns an array of {@link Pointer} of the requested size. */ +// public Pointer[] getPointerArray(long offset, int arraySize) { +// Pointer[] buf = new Pointer[arraySize]; +// read(offset, buf, 0, arraySize); +// return buf; +// } +// +// /**

Returns an array of String based on a native array +// * of char *. The array length is determined by a +// * NULL-valued terminating element. +// *

+// * The strings are decoded using the encoding returned by {@link +// * Native#getDefaultStringEncoding()}. +// */ +// public String[] getStringArray(long offset) { +// return getStringArray(offset, -1, Native.getDefaultStringEncoding()); +// } +// +// /** Returns an array of String based on a native array +// * of char *, using the requested encoding. The array length +// * is determined by a NULL-valued terminating element. +// */ +// public String[] getStringArray(long offset, String encoding) { +// return getStringArray(offset, -1, encoding); +// } +// +// /**

Returns an array of String based on a native array +// * of char *, using the given array length. +// *

+// * The strings are decoded using the encoding returned by {@link +// * Native#getDefaultStringEncoding()}. +// */ +// public String[] getStringArray(long offset, int length) { +// return getStringArray(offset, length, Native.getDefaultStringEncoding()); +// } +// +// public String[] getWideStringArray(long offset) { +// return getWideStringArray(offset, -1); +// } +// +// public String[] getWideStringArray(long offset, int length) { +// return getStringArray(offset, length, NativeString.WIDE_STRING); +// } +// +// /** Returns an array of String based on a native array +// * of char* or wchar_t* based on the +// * wide parameter, using the given array length. +// * @param offset +// * @param length +// * @param encoding +// */ +// public String[] getStringArray(long offset, int length, String encoding) { +// List strings = new ArrayList<>(); +// Pointer p; +// int addOffset = 0; +// if (length != -1) { +// p = getPointer(offset + addOffset); +// int count = 0; +// while (count++ < length) { +// String s = p == null +// ? null +// : (NativeString.WIDE_STRING.equals(encoding) +// ? p.getWideString(0) : p.getString(0, encoding)); +// strings.add(s); +// if (count < length) { +// addOffset += Native.POINTER_SIZE; +// p = getPointer(offset + addOffset); +// } +// } +// } else { +// while ((p = getPointer(offset + addOffset)) != null) { +// String s = NativeString.WIDE_STRING.equals(encoding) +// ? p.getWideString(0) +// : p.getString(0, encoding); +// strings.add(s); +// addOffset += Native.POINTER_SIZE; +// } +// } +// return strings.toArray(new String[0]); +// } +// +// ////////////////////////////////////////////////////////////////////////// +// // Java type write methods +// ////////////////////////////////////////////////////////////////////////// +// +// void setValue(long offset, Object value, Class type) { +// +// // Set the value at the offset according to its type +// if (type == boolean.class || type == Boolean.class) { +// setInt(offset, Boolean.TRUE.equals(value) ? -1 : 0); +// } else if (type == byte.class || type == Byte.class) { +// setByte(offset, value == null ? 0 : ((Byte)value).byteValue()); +// } else if (type == short.class || type == Short.class) { +// setShort(offset, value == null ? 0 : ((Short)value).shortValue()); +// } else if (type == char.class || type == Character.class) { +// setChar(offset, value == null ? 0 : ((Character)value).charValue()); +// } else if (type == int.class || type == Integer.class) { +// setInt(offset, value == null ? 0 : ((Integer)value).intValue()); +// } else if (type == long.class || type == Long.class) { +// setLong(offset, value == null ? 0 : ((Long)value).longValue()); +// } else if (type == float.class || type == Float.class) { +// setFloat(offset, value == null ? 0f : ((Float)value).floatValue()); +// } else if (type == double.class || type == Double.class) { +// setDouble(offset, value == null ? 0.0 : ((Double)value).doubleValue()); +// } else if (type == Pointer.class) { +// setPointer(offset, (Pointer)value); +// } else if (type == String.class) { +// setPointer(offset, (Pointer)value); +// } else if (type == WString.class) { +// setPointer(offset, (Pointer)value); +// } else if (Structure.class.isAssignableFrom(type)) { +// Structure s = (Structure)value; +// if (Structure.ByReference.class.isAssignableFrom(type)) { +// setPointer(offset, s == null ? null : s.getPointer()); +// if (s != null) { +// s.autoWrite(); +// } +// } +// else { +// s.useMemory(this, (int)offset, true); +// s.write(); +// } +// } else if (Callback.class.isAssignableFrom(type)) { +// setPointer(offset, CallbackReference.getFunctionPointer((Callback)value)); +// } else if (Platform.HAS_BUFFERS && Buffer.class.isAssignableFrom(type)) { +// Pointer p = value == null ? null +// : Native.getDirectBufferPointer((Buffer)value); +// setPointer(offset, p); +// } else if (NativeMapped.class.isAssignableFrom(type)) { +// NativeMappedConverter tc = NativeMappedConverter.getInstance(type); +// Class nativeType = tc.nativeType(); +// setValue(offset, tc.toNative(value, new ToNativeContext()), nativeType); +// } else if (type.isArray()) { +// writeArray(offset, value, type.getComponentType()); +// } else { +// throw new IllegalArgumentException("Writing " + type + " to memory is not supported"); +// } +// } +// +// /** Write memory starting at offset from the array with element type cls. */ +// private void writeArray(long offset, Object value, Class cls) { +// if (cls == byte.class) { +// byte[] buf = (byte[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == short.class) { +// short[] buf = (short[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == char.class) { +// char[] buf = (char[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == int.class) { +// int[] buf = (int[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == long.class) { +// long[] buf = (long[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == float.class) { +// float[] buf = (float[])value; +// write(offset, buf, 0, buf.length); +// } else if (cls == double.class) { +// double[] buf = (double[])value; +// write(offset, buf, 0, buf.length); +// } else if (Pointer.class.isAssignableFrom(cls)) { +// Pointer[] buf = (Pointer[])value; +// write(offset, buf, 0, buf.length); +// } else if (Structure.class.isAssignableFrom(cls)) { +// Structure[] sbuf = (Structure[])value; +// if (Structure.ByReference.class.isAssignableFrom(cls)) { +// Pointer[] buf = new Pointer[sbuf.length]; +// for (int i=0;i < sbuf.length;i++) { +// if (sbuf[i] == null) { +// buf[i] = null; +// } else { +// buf[i] = sbuf[i].getPointer(); +// sbuf[i].write(); +// } +// } +// write(offset, buf, 0, buf.length); +// } else { +// Structure first = sbuf[0]; +// if (first == null) { +// first = Structure.newInstance((Class) cls, share(offset)); +// sbuf[0] = first; +// } else { +// first.useMemory(this, (int)offset, true); +// } +// first.write(); +// Structure[] tmp = first.toArray(sbuf.length); +// for (int i=1;i < sbuf.length;i++) { +// if (sbuf[i] == null) { +// sbuf[i] = tmp[i]; +// } else { +// sbuf[i].useMemory(this, (int)(offset + i * sbuf[i].size()), true); +// } +// sbuf[i].write(); +// } +// } +// } else if (NativeMapped.class.isAssignableFrom(cls)) { +// NativeMapped[] buf = (NativeMapped[])value; +// NativeMappedConverter tc = NativeMappedConverter.getInstance(cls); +// Class nativeType = tc.nativeType(); +// int size = Native.getNativeSize(value.getClass(), value) / buf.length; +// for (int i=0;i < buf.length;i++) { +// Object element = tc.toNative(buf[i], new ToNativeContext()); +// setValue(offset + i*size, element, nativeType); +// } +// } else { +// throw new IllegalArgumentException("Writing array of " +// + cls + " to memory not supported"); +// } +// } +// +// /** Write value to the requested bank of memory. +// * @param offset byte offset from pointer to start +// * @param length number of bytes to write +// * @param value value to be written +// */ +// public void setMemory(long offset, long length, byte value) { +// Native.setMemory(this, this.peer, offset, length, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jbyte *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value must +// * be set +// * @param value byte value to set +// */ +// public void setByte(long offset, byte value) { +// Native.setByte(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jshort *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value must +// * be set +// * @param value short value to set +// */ +// public void setShort(long offset, short value) { +// Native.setShort(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((wchar_t *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value char value to set +// */ +// public void setChar(long offset, char value) { +// Native.setChar(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jint *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value must +// * be set +// * @param value int value to set +// */ +// public void setInt(long offset, int value) { +// Native.setInt(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jlong *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value long value to set +// */ +// public void setLong(long offset, long value) { +// Native.setLong(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((long *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value long value to set +// */ +// public void setNativeLong(long offset, NativeLong value) { +// if (NativeLong.SIZE == 8) { +// setLong(offset, value.longValue()); +// } else { +// setInt(offset, value.intValue()); +// } +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jfloat *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value float value to set +// */ +// public void setFloat(long offset, float value) { +// Native.setFloat(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((jdouble *)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value double value to set +// */ +// public void setDouble(long offset, double value) { +// Native.setDouble(this, this.peer, offset, value); +// } +// +// /** +// * Set value at location being pointed to. This is equivalent +// * to the expression +// * *((void **)((char *)Pointer + offset)) = value. +// * +// * @param offset byte offset from pointer at which value +// * must be set +// * @param value Pointer holding the actual pointer value to +// * set, which may be null to indicate a NULL +// * pointer. +// */ +// public void setPointer(long offset, Pointer value) { +// Native.setPointer(this, this.peer, offset, value != null ? value.peer : 0); +// } +// +// /** +// * Copy string value to the location being pointed to as a +// * wide string (wchar_t*). +// * +// * @param offset byte offset from pointer at which characters in +// * value must be set +// * @param value java.lang.String value to set +// */ +// public void setWideString(long offset, String value) { +// Native.setWideString(this, this.peer, offset, value); +// } +// +// /** +// * Copy string value to the location being pointed to as a +// * wide string (wchar_t*). +// * +// * @param offset byte offset from pointer at which characters in +// * value must be set +// * @param value WString value to set +// */ +// public void setString(long offset, WString value) { +// setWideString(offset, value == null ? null : value.toString()); +// } +// +// /** +// * Copy bytes out of string value to the location being +// * pointed to, using the encoding indicated by {@link +// * Native#getDefaultStringEncoding()}. +// * +// * @param offset byte offset from pointer at which characters in +// * value must be set +// * @param value java.lang.String value to set +// */ +// public void setString(long offset, String value) { +// setString(offset, value, Native.getDefaultStringEncoding()); +// } +// +// /** +// * Copy string value to the location being pointed to, using +// * the requested encoding. +// * +// * @param offset byte offset from pointer at which characters in +// * value must be set +// * @param value java.lang.String value to set +// * @param encoding desired encoding +// */ +// public void setString(long offset, String value, String encoding) { +// byte[] data = Native.getBytes(value, encoding); +// write(offset, data, 0, data.length); +// setByte(offset + data.length, (byte)0); +// } +// +// /** Dump memory for debugging purposes. */ +// public String dump(long offset, int size) { +// final int BYTES_PER_ROW = 4; +// final String TITLE = "memory dump"; +// // estimate initial size assuming a 2 char line separator +// StringWriter sw = new StringWriter(TITLE.length() + 2 + size * 2 + (size / BYTES_PER_ROW * 4)); +// PrintWriter out = new PrintWriter(sw); +// out.println(TITLE); +//// byte[] buf = getByteArray(offset, size); +// for (int i=0;i < size;i++) { +//// byte b = buf[i]; +// byte b = getByte(offset + i); +// if ((i % BYTES_PER_ROW) == 0) out.print("["); +// if (b >=0 && b < 16) +// out.print("0"); +// out.print(Integer.toHexString(b & 0xFF)); +// if ((i % BYTES_PER_ROW) == BYTES_PER_ROW-1 && i < size-1) +// out.println("]"); +// } +// if (sw.getBuffer().charAt(sw.getBuffer().length() - 2) != ']') { +// out.println("]"); +// } +// return sw.toString(); +// } +// +// @Override +// public String toString() { +// return "native@0x" + Long.toHexString(peer); +// } +// + /** Read the native peer value. Use with caution. */ + public static int nativeValue(Pointer p) { + return p == null ? 0 : p.peer; + } +// +// /** Set the native peer value. Use with caution. */ +// public static void nativeValue(Pointer p, long value) { +// p.peer = value; +// } +// +// /** Pointer which disallows all read/write access. */ +// private static class Opaque extends Pointer { +// private Opaque(long peer) { super(peer); } +// private final String MSG = "This pointer is opaque: " + this; +// @Override +// public Pointer share(long offset, long size) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void clear(long size) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public long indexOf(long offset, byte value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, byte[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, char[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, short[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, int[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, long[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, float[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, double[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void read(long bOff, Pointer[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, byte[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, char[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, short[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, int[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, long[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, float[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, double[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void write(long bOff, Pointer[] buf, int index, int length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public ByteBuffer getByteBuffer(long offset, long length) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public byte getByte(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public char getChar(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public short getShort(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public int getInt(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public long getLong(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public float getFloat(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public double getDouble(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public Pointer getPointer(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public String getString(long bOff, String encoding) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public String getWideString(long bOff) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setByte(long bOff, byte value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setChar(long bOff, char value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setShort(long bOff, short value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setInt(long bOff, int value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setLong(long bOff, long value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setFloat(long bOff, float value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setDouble(long bOff, double value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setPointer(long offset, Pointer value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setString(long offset, String value, String encoding) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setWideString(long offset, String value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public void setMemory(long offset, long size, byte value) { +// throw new UnsupportedOperationException(MSG); +// } +// @Override +// public String dump(long offset, int size) { +// throw new UnsupportedOperationException(MSG); +// } + @Override + public String toString() { + return "const@0x" + Integer.toHexString(peer); + } +// } +} diff --git a/sources/net.sf.j2s.java.core/src/java/awt/Font.java b/sources/net.sf.j2s.java.core/src/java/awt/Font.java index abe25d43f..936a5be3c 100644 --- a/sources/net.sf.j2s.java.core/src/java/awt/Font.java +++ b/sources/net.sf.j2s.java.core/src/java/awt/Font.java @@ -53,6 +53,7 @@ import static sun.font.EAttribute.EWIDTH; import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; import java.awt.font.LineMetrics; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; @@ -65,6 +66,8 @@ import java.text.CharacterIterator; import java.util.Map; +import com.sun.javafx.text.GlyphLayout; + import javajs.util.SB; import sun.font.AttributeMap; import sun.font.AttributeValues; @@ -72,6 +75,7 @@ import sun.font.Font2DHandle; import sun.font.FontDesignMetrics; import sun.font.FontLineMetrics; +import sun.font.StandardGlyphVector; import swingjs.JSToolkit; /** @@ -2578,139 +2582,139 @@ public Rectangle2D getMaxCharBounds(FontRenderContext frc) { metrics[0] + metrics[1] + metrics[2]); } -//// /** -//// * Creates a {@link java.awt.font.GlyphVector GlyphVector} by -//// * mapping characters to glyphs one-to-one based on the -//// * Unicode cmap in this Font. This method does no other -//// * processing besides the mapping of glyphs to characters. This -//// * means that this method is not useful for some scripts, such -//// * as Arabic, Hebrew, Thai, and Indic, that require reordering, -//// * shaping, or ligature substitution. -//// * @param frc the specified FontRenderContext -//// * @param str the specified String -//// * @return a new GlyphVector created with the -//// * specified String and the specified -//// * FontRenderContext. -//// */ -//// public GlyphVector createGlyphVector(FontRenderContext frc, String str) -//// { -//// return (GlyphVector)new StandardGlyphVector(this, str, frc); -//// } -//// -//// /** -//// * Creates a {@link java.awt.font.GlyphVector GlyphVector} by -//// * mapping characters to glyphs one-to-one based on the -//// * Unicode cmap in this Font. This method does no other -//// * processing besides the mapping of glyphs to characters. This -//// * means that this method is not useful for some scripts, such -//// * as Arabic, Hebrew, Thai, and Indic, that require reordering, -//// * shaping, or ligature substitution. -//// * @param frc the specified FontRenderContext -//// * @param chars the specified array of characters -//// * @return a new GlyphVector created with the -//// * specified array of characters and the specified -//// * FontRenderContext. -//// */ -//// public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) -//// { -//// return (GlyphVector)new StandardGlyphVector(this, chars, frc); -//// } -//// -//// /** -//// * Creates a {@link java.awt.font.GlyphVector GlyphVector} by -//// * mapping the specified characters to glyphs one-to-one based on the -//// * Unicode cmap in this Font. This method does no other -//// * processing besides the mapping of glyphs to characters. This -//// * means that this method is not useful for some scripts, such -//// * as Arabic, Hebrew, Thai, and Indic, that require reordering, -//// * shaping, or ligature substitution. -//// * @param frc the specified FontRenderContext -//// * @param ci the specified CharacterIterator -//// * @return a new GlyphVector created with the -//// * specified CharacterIterator and the specified -//// * FontRenderContext. -//// */ -//// public GlyphVector createGlyphVector( FontRenderContext frc, -//// CharacterIterator ci) -//// { -//// return (GlyphVector)new StandardGlyphVector(this, ci, frc); -//// } -//// -//// /** -//// * Creates a {@link java.awt.font.GlyphVector GlyphVector} by -//// * mapping characters to glyphs one-to-one based on the -//// * Unicode cmap in this Font. This method does no other -//// * processing besides the mapping of glyphs to characters. This -//// * means that this method is not useful for some scripts, such -//// * as Arabic, Hebrew, Thai, and Indic, that require reordering, -//// * shaping, or ligature substitution. -//// * @param frc the specified FontRenderContext -//// * @param glyphCodes the specified integer array -//// * @return a new GlyphVector created with the -//// * specified integer array and the specified -//// * FontRenderContext. -//// */ -//// public GlyphVector createGlyphVector( FontRenderContext frc, -//// int [] glyphCodes) -//// { -//// return (GlyphVector)new StandardGlyphVector(this, glyphCodes, frc); -//// } -//// -//// /** -//// * Returns a new GlyphVector object, performing full -//// * layout of the text if possible. Full layout is required for -//// * complex text, such as Arabic or Hindi. Support for different -//// * scripts depends on the font and implementation. -//// *

-//// * Layout requires bidi analysis, as performed by -//// * Bidi, and should only be performed on text that -//// * has a uniform direction. The direction is indicated in the -//// * flags parameter,by using LAYOUT_RIGHT_TO_LEFT to indicate a -//// * right-to-left (Arabic and Hebrew) run direction, or -//// * LAYOUT_LEFT_TO_RIGHT to indicate a left-to-right (English) -//// * run direction. -//// *

-//// * In addition, some operations, such as Arabic shaping, require -//// * context, so that the characters at the start and limit can have -//// * the proper shapes. Sometimes the data in the buffer outside -//// * the provided range does not have valid data. The values -//// * LAYOUT_NO_START_CONTEXT and LAYOUT_NO_LIMIT_CONTEXT can be -//// * added to the flags parameter to indicate that the text before -//// * start, or after limit, respectively, should not be examined -//// * for context. -//// *

-//// * All other values for the flags parameter are reserved. -//// * -//// * @param frc the specified FontRenderContext -//// * @param text the text to layout -//// * @param start the start of the text to use for the GlyphVector -//// * @param limit the limit of the text to use for the GlyphVector -//// * @param flags control flags as described above -//// * @return a new GlyphVector representing the text between -//// * start and limit, with glyphs chosen and positioned so as to best represent -//// * the text -//// * @throws ArrayIndexOutOfBoundsException if start or limit is -//// * out of bounds -//// * @see java.text.Bidi -//// * @see #LAYOUT_LEFT_TO_RIGHT -//// * @see #LAYOUT_RIGHT_TO_LEFT -//// * @see #LAYOUT_NO_START_CONTEXT -//// * @see #LAYOUT_NO_LIMIT_CONTEXT -//// * @since 1.4 -//// */ -//// public GlyphVector layoutGlyphVector(FontRenderContext frc, -//// char[] text, -//// int start, -//// int limit, -//// int flags) { -//// -//// GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines -//// StandardGlyphVector gv = gl.layout(this, frc, text, -//// start, limit-start, flags, null); -//// GlyphLayout.done(gl); -//// return gv; -//// } + /** + * Creates a {@link java.awt.font.GlyphVector GlyphVector} by + * mapping characters to glyphs one-to-one based on the + * Unicode cmap in this Font. This method does no other + * processing besides the mapping of glyphs to characters. This + * means that this method is not useful for some scripts, such + * as Arabic, Hebrew, Thai, and Indic, that require reordering, + * shaping, or ligature substitution. + * @param frc the specified FontRenderContext + * @param str the specified String + * @return a new GlyphVector created with the + * specified String and the specified + * FontRenderContext. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, String str) + { + return (GlyphVector)new StandardGlyphVector(this, str, frc); + } + + /** + * Creates a {@link java.awt.font.GlyphVector GlyphVector} by + * mapping characters to glyphs one-to-one based on the + * Unicode cmap in this Font. This method does no other + * processing besides the mapping of glyphs to characters. This + * means that this method is not useful for some scripts, such + * as Arabic, Hebrew, Thai, and Indic, that require reordering, + * shaping, or ligature substitution. + * @param frc the specified FontRenderContext + * @param chars the specified array of characters + * @return a new GlyphVector created with the + * specified array of characters and the specified + * FontRenderContext. + */ + public GlyphVector createGlyphVector(FontRenderContext frc, char[] chars) + { + return (GlyphVector)new StandardGlyphVector(this, chars, frc); + } + /** + * Creates a {@link java.awt.font.GlyphVector GlyphVector} by + * mapping the specified characters to glyphs one-to-one based on the + * Unicode cmap in this Font. This method does no other + * processing besides the mapping of glyphs to characters. This + * means that this method is not useful for some scripts, such + * as Arabic, Hebrew, Thai, and Indic, that require reordering, + * shaping, or ligature substitution. + * @param frc the specified FontRenderContext + * @param ci the specified CharacterIterator + * @return a new GlyphVector created with the + * specified CharacterIterator and the specified + * FontRenderContext. + */ + public GlyphVector createGlyphVector( FontRenderContext frc, + CharacterIterator ci) + { + return (GlyphVector)new StandardGlyphVector(this, ci, frc); + } + + /** + * Creates a {@link java.awt.font.GlyphVector GlyphVector} by + * mapping characters to glyphs one-to-one based on the + * Unicode cmap in this Font. This method does no other + * processing besides the mapping of glyphs to characters. This + * means that this method is not useful for some scripts, such + * as Arabic, Hebrew, Thai, and Indic, that require reordering, + * shaping, or ligature substitution. + * @param frc the specified FontRenderContext + * @param glyphCodes the specified integer array + * @return a new GlyphVector created with the + * specified integer array and the specified + * FontRenderContext. + */ + public GlyphVector createGlyphVector( FontRenderContext frc, + int [] glyphCodes) + { + return (GlyphVector)new StandardGlyphVector(this, glyphCodes, frc); + } + + /** + * Returns a new GlyphVector object, performing full + * layout of the text if possible. Full layout is required for + * complex text, such as Arabic or Hindi. Support for different + * scripts depends on the font and implementation. + *

+ * Layout requires bidi analysis, as performed by + * Bidi, and should only be performed on text that + * has a uniform direction. The direction is indicated in the + * flags parameter,by using LAYOUT_RIGHT_TO_LEFT to indicate a + * right-to-left (Arabic and Hebrew) run direction, or + * LAYOUT_LEFT_TO_RIGHT to indicate a left-to-right (English) + * run direction. + *

+ * In addition, some operations, such as Arabic shaping, require + * context, so that the characters at the start and limit can have + * the proper shapes. Sometimes the data in the buffer outside + * the provided range does not have valid data. The values + * LAYOUT_NO_START_CONTEXT and LAYOUT_NO_LIMIT_CONTEXT can be + * added to the flags parameter to indicate that the text before + * start, or after limit, respectively, should not be examined + * for context. + *

+ * All other values for the flags parameter are reserved. + * + * @param frc the specified FontRenderContext + * @param text the text to layout + * @param start the start of the text to use for the GlyphVector + * @param limit the limit of the text to use for the GlyphVector + * @param flags control flags as described above + * @return a new GlyphVector representing the text between + * start and limit, with glyphs chosen and positioned so as to best represent + * the text + * @throws ArrayIndexOutOfBoundsException if start or limit is + * out of bounds + * @see java.text.Bidi + * @see #LAYOUT_LEFT_TO_RIGHT + * @see #LAYOUT_RIGHT_TO_LEFT + * @see #LAYOUT_NO_START_CONTEXT + * @see #LAYOUT_NO_LIMIT_CONTEXT + * @since 1.4 + */ +// public GlyphVector layoutGlyphVector(FontRenderContext frc, +// char[] text, +// int start, +// int limit, +// int flags) { +// +// GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines +// StandardGlyphVector gv = gl.layout(this, frc, text, +// start, limit-start, flags, null); +// GlyphLayout.done(gl); +// return gv; +// } +// /** * A flag to layoutGlyphVector indicating that text is left-to-right as * determined by Bidi analysis. diff --git a/sources/net.sf.j2s.java.core/src/java/awt/image/BufferedImage.java b/sources/net.sf.j2s.java.core/src/java/awt/image/BufferedImage.java index 25013de75..e024457d2 100644 --- a/sources/net.sf.j2s.java.core/src/java/awt/image/BufferedImage.java +++ b/sources/net.sf.j2s.java.core/src/java/awt/image/BufferedImage.java @@ -2126,13 +2126,13 @@ private static void toIntARGB(byte[] ctxData, int[] iData, int alpha) { } private void toByteABGR(byte[] ctxData, byte[] buf) { - // convert canvas [r g b a r g b a ...] into [ b g r a b g r a b g r a...] + // convert canvas [r g b a r g b a ...] into [ a b g r a b g r a b g r ...] int n = ctxData.length; for (int i = 0, j = 0; i < n; j += 4) { + buf[i++] = ctxData[j + 3]; buf[i++] = ctxData[j + 2]; buf[i++] = ctxData[j + 1]; - buf[i++] = ctxData[j]; - buf[i++] = ctxData[j + 3]; + buf[i++] = ctxData[j + 0]; } } diff --git a/sources/net.sf.j2s.java.core/src/java/lang/Class.java b/sources/net.sf.j2s.java.core/src/java/lang/Class.java index 34b96808a..d75c43275 100644 --- a/sources/net.sf.j2s.java.core/src/java/lang/Class.java +++ b/sources/net.sf.j2s.java.core/src/java/lang/Class.java @@ -2424,8 +2424,10 @@ public InputStream getResourceAsStream(String name) { return null; name = name.replace('\\','/'); String baseFolder = null; - String fname= name; + String fname = name; URL url = null; + Map fileCache = null; + String javapath = ""; if (name.startsWith(File.temporaryDirectory)) { data = JSUtil.getCachedFileData(name, true); if (data == null) @@ -2443,10 +2445,9 @@ public InputStream getResourceAsStream(String name) { baseFolder = Clazz._Loader.getJ2SLibBase(); if (baseFolder.charAt(baseFolder.length - 1) != '/') baseFolder += "/"; - fname = baseFolder + name.substring (1); } else { baseFolder = Clazz._Loader.getJ2SLibBase(); // getClass().getClassLoader() uses full path - fname = baseFolder; + fname = ""; if (this.$_$base == null) { // getClass().getResource() will be here var pkgs = clazzName.split("."); @@ -2461,31 +2462,33 @@ public InputStream getResourceAsStream(String name) { } fname += name; } - var javapath = fname; + fname = fname.substring(1); + javapath = fname; + fname = baseFolder + fname; try { -// if (fname.indexOf(":/") < 0) { -// var d = document.location.href.split("#")[0].split("?")[0].split("/"); -// d[d.length - 1] = fname; -// fname = d.join("/"); -// } Clazz.load("java.net.URL"); url = Clazz.new_(java.net.URL.c$$S,["file:/" + fname]); } catch (e) { return null; } - var fileCache = J2S.getSetJavaFileCache(null); - data = fileCache && fileCache.get$O(javapath); + fileCache = J2S.getSetJavaFileCache(null); */ } - if (data == null) - data = JSUtil.J2S.getFileData(fname.toString(),null,true,true); + data = (fileCache == null ? null : fileCache.get(javapath)); + if (data == null && fileCache != null) { + data = fileCache.get(fname); + } + if (data == null) { + data = JSUtil.J2S.getFileData(fname,null,true,true); + } /** * @j2sNative * if (data == null || data == Boolean.FALSE || data == "error" || data.indexOf && data.indexOf("[Exception") == 0) return null; - var bytes = (data.__BYTESIZE == 1 ? data : J2S._strToBytes(data)); + // could be byte[] or BArray or String + var bytes = (data.__BYTESIZE == 1 ? data : data.data ? data.data : J2S._strToBytes(data)); Clazz.load("java.io.BufferedInputStream"); Clazz.load("java.io.ByteArrayInputStream"); var is = Clazz.new_(java.io.BufferedInputStream.c$$java_io_InputStream, [Clazz.new_(java.io.ByteArrayInputStream.c$$BA, [bytes])]); @@ -2496,14 +2499,6 @@ public InputStream getResourceAsStream(String name) { { return null; } -// -// name = resolveName(name); -// ClassLoader cl = getClassLoader0(); -// if (cl == null) { -// // A system class. -// return ClassLoader.getSystemResourceAsStream(name); -// } -// return cl.getResourceAsStream(name); } /** @@ -3053,7 +3048,9 @@ private Method[] privateGetPublicMethods(boolean isAll) { // interface hack ms = new Method[$methodList$.length]; for (int i = ms.length; --i >= 0;) { - ms[i] = new Method(this, $methodList$[i], null, Void.class, null, java.lang.reflect.Modifier.PUBLIC); + String name = $methodList$[i]; + boolean isNative = (/** @j2sNative this.$clazz$[o.exName].isNative ||*/ false); + ms[i] = new Method(this, name, null, Void.class, null, getMods(isNative)); } return ms; } @@ -3061,6 +3058,8 @@ private Method[] privateGetPublicMethods(boolean isAll) { ms = new Method[0]; String attr = null; Object o = null; + boolean isNative = false; + // JUST $xxxx... names: o.exName.indexOf("$") != 0 /** * @j2sNative * @@ -3074,12 +3073,13 @@ private Method[] privateGetPublicMethods(boolean isAll) { * && o != this.$clazz$[attr] * && (isAll || o.exClazz == this.$clazz$) * && !o.exName.startsWith("c$") - * ) { // there are polynormical methods. + * ) { + * isNative = o.isNative; */ - Method m = new Method(this, attr, UNKNOWN_PARAMETERS, Void.class, NO_PARAMETERS, Modifier.PUBLIC); - m._setJSMethod(o, Modifier.PUBLIC); + Method m = new Method(this, attr, UNKNOWN_PARAMETERS, Void.class, NO_PARAMETERS, getMods(isNative)); + m._setJSMethod(o, Modifier.PUBLIC); /** * @j2sNative * @@ -3093,8 +3093,10 @@ private Method[] privateGetPublicMethods(boolean isAll) { * && o.exName.indexOf("$") != 0 * && !o.exName.startsWith("c$") * ) { + * isNative = o.isNative; */ - m = new Method(this, attr, UNKNOWN_PARAMETERS, Void.class, NO_PARAMETERS, Modifier.PUBLIC); + // NOT $xxxx... names: o.exName.indexOf("$") != 0 + m = new Method(this, attr, UNKNOWN_PARAMETERS, Void.class, NO_PARAMETERS, getMods(isNative)); m._setJSMethod(o, Modifier.STATIC); /** * @j2sNative @@ -3103,9 +3105,8 @@ private Method[] privateGetPublicMethods(boolean isAll) { * }} */ - return ms; + return ms; - // checkInitted(); // Method[] res = null; // if (useCaches) { @@ -3167,6 +3168,14 @@ private Method[] privateGetPublicMethods(boolean isAll) { // return res; } + private int getMods(boolean isNative) { + int mods = Modifier.PUBLIC; + if (isNative) + mods |= Modifier.NATIVE; + return mods; + } + + public Constructor[] $constructors$; diff --git a/sources/net.sf.j2s.java.core/src/java/lang/reflect/Field.java b/sources/net.sf.j2s.java.core/src/java/lang/reflect/Field.java index 012d47f75..4dc4d75e5 100644 --- a/sources/net.sf.j2s.java.core/src/java/lang/reflect/Field.java +++ b/sources/net.sf.j2s.java.core/src/java/lang/reflect/Field.java @@ -477,7 +477,7 @@ String getSignature() { public Class getType() { if (myType == null || myType instanceof Class) return (Class) myType; - return (Class) (myType = AnnotationParser.JSAnnotationObject.typeForString(myType.toString(), false)); + return (Class) (myType = AnnotationParser.JSAnnotationObject.typeForString(myType.toString(), true)); } diff --git a/sources/net.sf.j2s.java.core/src/java/lang/reflect/Method.java b/sources/net.sf.j2s.java.core/src/java/lang/reflect/Method.java index 13b859a32..7c4f2ed09 100644 --- a/sources/net.sf.j2s.java.core/src/java/lang/reflect/Method.java +++ b/sources/net.sf.j2s.java.core/src/java/lang/reflect/Method.java @@ -393,7 +393,7 @@ public Class getReturnType() { // SwingJS will store this as a String to avoid unnecessary class loading if (returnType == null || returnType instanceof Class) return (Class) returnType; - return (Class) (returnType = AnnotationParser.JSAnnotationObject.typeForString(returnType.toString(), false)); + return (Class) (returnType = AnnotationParser.JSAnnotationObject.typeForString(returnType.toString(), true)); } diff --git a/sources/net.sf.j2s.java.core/src/java/text/DateFormat.java b/sources/net.sf.j2s.java.core/src/java/text/DateFormat.java index 4c489126e..8a02267a8 100644 --- a/sources/net.sf.j2s.java.core/src/java/text/DateFormat.java +++ b/sources/net.sf.j2s.java.core/src/java/text/DateFormat.java @@ -336,7 +336,7 @@ public abstract StringBuffer format(Date date, StringBuffer toAppendTo, * @param date the time value to be formatted into a time string. * @return the formatted time string. */ - public final String format(Date date) + public String format(Date date) { return format(date, new StringBuffer(), DontCareFieldPosition.INSTANCE).toString(); diff --git a/sources/net.sf.j2s.java.core/src/java/text/Format.java b/sources/net.sf.j2s.java.core/src/java/text/Format.java index 488095db0..82bfc40db 100644 --- a/sources/net.sf.j2s.java.core/src/java/text/Format.java +++ b/sources/net.sf.j2s.java.core/src/java/text/Format.java @@ -139,6 +139,10 @@ public abstract class Format implements Serializable, Cloneable { private static final long serialVersionUID = -299282585814624189L; + protected boolean isSimpleMMDDYY; + + + /** * Sole constructor. (For invocation by subclass constructors, typically @@ -159,8 +163,8 @@ protected Format() { * @exception IllegalArgumentException if the Format cannot format the given * object */ - public final String format (Object obj) { - return format(obj, new StringBuffer(), new FieldPosition(0)).toString(); + public /* swingjs final*/ String format (Object obj) { + return format(obj, new StringBuffer(), (isSimpleMMDDYY ? null : new FieldPosition(0))).toString(); } /** diff --git a/sources/net.sf.j2s.java.core/src/java/text/SimpleDateFormat.java b/sources/net.sf.j2s.java.core/src/java/text/SimpleDateFormat.java index 79df57908..3174b7e83 100644 --- a/sources/net.sf.j2s.java.core/src/java/text/SimpleDateFormat.java +++ b/sources/net.sf.j2s.java.core/src/java/text/SimpleDateFormat.java @@ -55,205 +55,195 @@ import java.util.Map; import java.util.ResourceBundle; import java.util.TimeZone; + +import sun.util.BuddhistCalendar; import sun.util.calendar.CalendarUtils; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.resources.LocaleData; //import sun.util.calendar.ZoneInfoFile; /** - * SimpleDateFormat is a concrete class for formatting and - * parsing dates in a locale-sensitive manner. It allows for formatting - * (date -> text), parsing (text -> date), and normalization. + * SimpleDateFormat is a concrete class for formatting and parsing + * dates in a locale-sensitive manner. It allows for formatting (date -> text), + * parsing (text -> date), and normalization. * *

- * SimpleDateFormat allows you to start by choosing - * any user-defined patterns for date-time formatting. However, you - * are encouraged to create a date-time formatter with either - * getTimeInstance, getDateInstance, or - * getDateTimeInstance in DateFormat. Each - * of these class methods can return a date/time formatter initialized - * with a default format pattern. You may modify the format pattern - * using the applyPattern methods as desired. - * For more information on using these methods, see - * {@link DateFormat}. + * SimpleDateFormat allows you to start by choosing any + * user-defined patterns for date-time formatting. However, you are encouraged + * to create a date-time formatter with either getTimeInstance, + * getDateInstance, or getDateTimeInstance in + * DateFormat. Each of these class methods can return a date/time + * formatter initialized with a default format pattern. You may modify the + * format pattern using the applyPattern methods as desired. For + * more information on using these methods, see {@link DateFormat}. * *

Date and Time Patterns

*

* Date and time formats are specified by date and time pattern - * strings. - * Within date and time pattern strings, unquoted letters from + * strings. Within date and time pattern strings, unquoted letters from * 'A' to 'Z' and from 'a' to * 'z' are interpreted as pattern letters representing the - * components of a date or time string. - * Text can be quoted using single quotes (') to avoid - * interpretation. - * "''" represents a single quote. - * All other characters are not interpreted; they're simply copied into the - * output string during formatting or matched against the input string + * components of a date or time string. Text can be quoted using single quotes + * (') to avoid interpretation. "''" represents a + * single quote. All other characters are not interpreted; they're simply copied + * into the output string during formatting or matched against the input string * during parsing. *

* The following pattern letters are defined (all other characters from * 'A' to 'Z' and from 'a' to - * 'z' are reserved): - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Letter - * Date or Time Component - * Presentation - * Examples - *
G - * Era designator - * Text - * AD - *
y - * Year - * Year - * 1996; 96 - *
M - * Month in year - * Month - * July; Jul; 07 - *
w - * Week in year - * Number - * 27 - *
W - * Week in month - * Number - * 2 - *
D - * Day in year - * Number - * 189 - *
d - * Day in month - * Number - * 10 - *
F - * Day of week in month - * Number - * 2 - *
E - * Day in week - * Text - * Tuesday; Tue - *
a - * Am/pm marker - * Text - * PM - *
H - * Hour in day (0-23) - * Number - * 0 - *
k - * Hour in day (1-24) - * Number - * 24 - *
K - * Hour in am/pm (0-11) - * Number - * 0 - *
h - * Hour in am/pm (1-12) - * Number - * 12 - *
m - * Minute in hour - * Number - * 30 - *
s - * Second in minute - * Number - * 55 - *
S - * Millisecond - * Number - * 978 - *
z - * Time zone - * General time zone - * Pacific Standard Time; PST; GMT-08:00 - *
Z - * Time zone - * RFC 822 time zone - * -0800 + * 'z' are reserved):
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Letter + * Date or Time Component + * Presentation + * Examples + *
G + * Era designator + * Text + * AD + *
y + * Year + * Year + * 1996; 96 + *
M + * Month in year + * Month + * July; Jul; 07 + *
w + * Week in year + * Number + * 27 + *
W + * Week in month + * Number + * 2 + *
D + * Day in year + * Number + * 189 + *
d + * Day in month + * Number + * 10 + *
F + * Day of week in month + * Number + * 2 + *
E + * Day in week + * Text + * Tuesday; Tue + *
a + * Am/pm marker + * Text + * PM + *
H + * Hour in day (0-23) + * Number + * 0 + *
k + * Hour in day (1-24) + * Number + * 24 + *
K + * Hour in am/pm (0-11) + * Number + * 0 + *
h + * Hour in am/pm (1-12) + * Number + * 12 + *
m + * Minute in hour + * Number + * 30 + *
s + * Second in minute + * Number + * 55 + *
S + * Millisecond + * Number + * 978 + *
z + * Time zone + * General time zone + * Pacific Standard Time; PST; + * GMT-08:00 + *
Z + * Time zone + * RFC 822 time zone + * -0800 *
- *
- * Pattern letters are usually repeated, as their number determines the - * exact presentation: + * Pattern letters are usually repeated, as their number + * determines the exact presentation: *
    - *
  • Text: - * For formatting, if the number of pattern letters is 4 or more, - * the full form is used; otherwise a short or abbreviated form - * is used if available. - * For parsing, both forms are accepted, independent of the number - * of pattern letters. - *
  • Number: - * For formatting, the number of pattern letters is the minimum - * number of digits, and shorter numbers are zero-padded to this amount. - * For parsing, the number of pattern letters is ignored unless - * it's needed to separate two adjacent fields. - *
  • Year: - * If the formatter's {@link #getCalendar() Calendar} is the Gregorian - * calendar, the following rules are applied.
    - *
      - *
    • For formatting, if the number of pattern letters is 2, the year - * is truncated to 2 digits; otherwise it is interpreted as a - * number. - *
    • For parsing, if the number of pattern letters is more than 2, - * the year is interpreted literally, regardless of the number of - * digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to - * Jan 11, 12 A.D. - *
    • For parsing with the abbreviated year pattern ("y" or "yy"), - * SimpleDateFormat must interpret the abbreviated year - * relative to some century. It does this by adjusting dates to be - * within 80 years before and 20 years after the time the SimpleDateFormat - * instance is created. For example, using a pattern of "MM/dd/yy" and a - * SimpleDateFormat instance created on Jan 1, 1997, the string - * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64" - * would be interpreted as May 4, 1964. - * During parsing, only strings consisting of exactly two digits, as defined by - * {@link Character#isDigit(char)}, will be parsed into the default century. - * Any other numeric string, such as a one digit string, a three or more digit - * string, or a two digit string that isn't all digits (for example, "-1"), is - * interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the - * same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC. - *
    - * Otherwise, calendar system specific forms are applied. - * For both formatting and parsing, if the number of pattern - * letters is 4 or more, a calendar specific {@linkplain - * Calendar#LONG long form} is used. Otherwise, a calendar - * specific {@linkplain Calendar#SHORT short or abbreviated form} - * is used. - *
  • Month: - * If the number of pattern letters is 3 or more, the month is - * interpreted as text; otherwise, - * it is interpreted as a number. - *
  • General time zone: - * Time zones are interpreted as text if they have - * names. For time zones representing a GMT offset value, the - * following syntax is used: - *
    + * 
  • Text: For formatting, if the number + * of pattern letters is 4 or more, the full form is used; otherwise a short or + * abbreviated form is used if available. For parsing, both forms are accepted, + * independent of the number of pattern letters. + *
  • Number: For formatting, the number + * of pattern letters is the minimum number of digits, and shorter numbers are + * zero-padded to this amount. For parsing, the number of pattern letters is + * ignored unless it's needed to separate two adjacent fields. + *
  • Year: If the formatter's + * {@link #getCalendar() Calendar} is the Gregorian calendar, the following + * rules are applied.
    + *
      + *
    • For formatting, if the number of pattern letters is 2, the year is + * truncated to 2 digits; otherwise it is interpreted as a + * number. + *
    • For parsing, if the number of pattern letters is more than 2, the year is + * interpreted literally, regardless of the number of digits. So using the + * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D. + *
    • For parsing with the abbreviated year pattern ("y" or "yy"), + * SimpleDateFormat must interpret the abbreviated year relative to + * some century. It does this by adjusting dates to be within 80 years before + * and 20 years after the time the SimpleDateFormat instance is + * created. For example, using a pattern of "MM/dd/yy" and a + * SimpleDateFormat instance created on Jan 1, 1997, the string + * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64" + * would be interpreted as May 4, 1964. During parsing, only strings consisting + * of exactly two digits, as defined by {@link Character#isDigit(char)}, will be + * parsed into the default century. Any other numeric string, such as a one + * digit string, a three or more digit string, or a two digit string that isn't + * all digits (for example, "-1"), is interpreted literally. So "01/02/3" or + * "01/02/003" are parsed, using the same pattern, as Jan 2, 3 AD. Likewise, + * "01/02/-3" is parsed as Jan 2, 4 BC. + *
    + * Otherwise, calendar system specific forms are applied. For both formatting + * and parsing, if the number of pattern letters is 4 or more, a calendar + * specific {@linkplain Calendar#LONG long form} is used. Otherwise, a calendar + * specific {@linkplain Calendar#SHORT short or abbreviated form} is used. + *
  • Month: If the number of pattern + * letters is 3 or more, the month is interpreted as text; + * otherwise, it is interpreted as a number. + *
  • General time zone: Time zones are + * interpreted as text if they have names. For time zones + * representing a GMT offset value, the following syntax is used: + * + *
      *     GMTOffsetTimeZone:
      *             GMT Sign Hours : Minutes
      *     Sign: one of
    @@ -264,23 +254,29 @@
      *     Minutes:
      *             Digit Digit
      *     Digit: one of
    - *             0 1 2 3 4 5 6 7 8 9
    - * Hours must be between 0 and 23, and Minutes must be between - * 00 and 59. The format is locale independent and digits must be taken - * from the Basic Latin block of the Unicode standard. - *

    For parsing, RFC 822 time zones are also - * accepted. - *

  • RFC 822 time zone: - * For formatting, the RFC 822 4-digit time zone format is used: - *
    + *             0 1 2 3 4 5 6 7 8 9
    + * 
    + * + * Hours must be between 0 and 23, and Minutes must be between 00 + * and 59. The format is locale independent and digits must be taken from the + * Basic Latin block of the Unicode standard. + *

    + * For parsing, RFC 822 time zones are also + * accepted. + *

  • RFC 822 time zone: For + * formatting, the RFC 822 4-digit time zone format is used: + * + *
      *     RFC822TimeZone:
      *             Sign TwoDigitHours Minutes
      *     TwoDigitHours:
    - *             Digit Digit
    - * TwoDigitHours must be between 00 and 23. Other definitions - * are as for general time zones. - *

    For parsing, general time zones are also - * accepted. + * Digit Digit + *

  • + * + * TwoDigitHours must be between 00 and 23. Other definitions are as for + * general time zones. + *

    + * For parsing, general time zones are also accepted. *

* SimpleDateFormat also supports localized date and time * pattern strings. In these strings, the pattern letters described above @@ -291,265 +287,254 @@ * *

Examples

* - * The following examples show how date and time patterns are interpreted in - * the U.S. locale. The given date and time are 2001-07-04 12:08:56 local time - * in the U.S. Pacific Time time zone. - *
- * - * - * - * - * - * - * - * - * - * - * - *
Date and Time Pattern - * Result - *
"yyyy.MM.dd G 'at' HH:mm:ss z" - * 2001.07.04 AD at 12:08:56 PDT - *
"EEE, MMM d, ''yy" - * Wed, Jul 4, '01 - *
"h:mm a" - * 12:08 PM - *
"hh 'o''clock' a, zzzz" - * 12 o'clock PM, Pacific Daylight Time - *
"K:mm a, z" - * 0:08 PM, PDT - *
"yyyyy.MMMMM.dd GGG hh:mm aaa" - * 02001.July.04 AD 12:08 PM - *
"EEE, d MMM yyyy HH:mm:ss Z" - * Wed, 4 Jul 2001 12:08:56 -0700 - *
"yyMMddHHmmssZ" - * 010704120856-0700 - *
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" - * 2001-07-04T12:08:56.235-0700 + * The following examples show how date and time patterns are interpreted in the + * U.S. locale. The given date and time are 2001-07-04 12:08:56 local time in + * the U.S. Pacific Time time zone.
+ * + * + * + * + * + * + * + * + * + * + * + *
Date and Time Pattern + * Result + *
"yyyy.MM.dd G 'at' HH:mm:ss z" + * 2001.07.04 AD at 12:08:56 PDT + *
"EEE, MMM d, ''yy" + * Wed, Jul 4, '01 + *
"h:mm a" + * 12:08 PM + *
"hh 'o''clock' a, zzzz" + * 12 o'clock PM, Pacific Daylight Time + *
"K:mm a, z" + * 0:08 PM, PDT + *
"yyyyy.MMMMM.dd GGG hh:mm aaa" + * 02001.July.04 AD 12:08 PM + *
"EEE, d MMM yyyy HH:mm:ss Z" + * Wed, 4 Jul 2001 12:08:56 -0700 + *
"yyMMddHHmmssZ" + * 010704120856-0700 + *
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" + * 2001-07-04T12:08:56.235-0700 *
*
* *

Synchronization

* *

- * Date formats are not synchronized. - * It is recommended to create separate format instances for each thread. - * If multiple threads access a format concurrently, it must be synchronized - * externally. + * Date formats are not synchronized. It is recommended to create separate + * format instances for each thread. If multiple threads access a format + * concurrently, it must be synchronized externally. * - * @see Java Tutorial - * @see java.util.Calendar - * @see java.util.TimeZone - * @see DateFormat - * @see DateFormatSymbols - * @author Mark Davis, Chen-Lieh Huang, Alan Liu + * @see Java + * Tutorial + * @see java.util.Calendar + * @see java.util.TimeZone + * @see DateFormat + * @see DateFormatSymbols + * @author Mark Davis, Chen-Lieh Huang, Alan Liu */ @SuppressWarnings("unused") public class SimpleDateFormat extends DateFormat { - // the official serial version ID which says cryptically - // which version we're compatible with - static final long serialVersionUID = 4774881970558875024L; - - // the internal serial version which says which version was written - // - 0 (default) for version up to JDK 1.1.3 - // - 1 for version from JDK 1.1.4, which includes a new field - static final int currentSerialVersion = 1; - - /** - * The version of the serialized data on the stream. Possible values: - *

    - *
  • 0 or not present on stream: JDK 1.1.3. This version - * has no defaultCenturyStart on stream. - *
  • 1 JDK 1.1.4 or later. This version adds - * defaultCenturyStart. - *
- * When streaming out this class, the most recent format - * and the highest allowable serialVersionOnStream - * is written. - * @serial - * @since JDK1.1.4 - */ - private int serialVersionOnStream = currentSerialVersion; - - /** - * The pattern string of this formatter. This is always a non-localized - * pattern. May not be null. See class documentation for details. - * @serial - */ - private String pattern; - - /** - * The compiled pattern. - */ - transient private char[] compiledPattern; - - /** - * Tags for the compiled pattern. - */ - private final static int TAG_QUOTE_ASCII_CHAR = 100; - private final static int TAG_QUOTE_CHARS = 101; - - /** - * Locale dependent digit zero. - * @see #zeroPaddingNumber - * @see java.text.DecimalFormatSymbols#getZeroDigit - */ - transient private char zeroDigit; - - /** - * The symbols used by this formatter for week names, month names, - * etc. May not be null. - * @serial - * @see java.text.DateFormatSymbols - */ - private DateFormatSymbols formatData; - - /** - * We map dates with two-digit years into the century starting at - * defaultCenturyStart, which may be any date. May - * not be null. - * @serial - * @since JDK1.1.4 - */ - private Date defaultCenturyStart; - - transient private int defaultCenturyStartYear; - - private static final int millisPerHour = 60 * 60 * 1000; - private static final int millisPerMinute = 60 * 1000; - - // For time zones that have no names, use strings GMT+minutes and - // GMT-minutes. For instance, in France the time zone is GMT+60. + // the official serial version ID which says cryptically + // which version we're compatible with + static final long serialVersionUID = 4774881970558875024L; + + // the internal serial version which says which version was written + // - 0 (default) for version up to JDK 1.1.3 + // - 1 for version from JDK 1.1.4, which includes a new field + static final int currentSerialVersion = 1; + + /** + * The version of the serialized data on the stream. Possible values: + *
    + *
  • 0 or not present on stream: JDK 1.1.3. This version has no + * defaultCenturyStart on stream. + *
  • 1 JDK 1.1.4 or later. This version adds + * defaultCenturyStart. + *
+ * When streaming out this class, the most recent format and the highest + * allowable serialVersionOnStream is written. + * + * @serial + * @since JDK1.1.4 + */ + private int serialVersionOnStream = currentSerialVersion; + + /** + * The pattern string of this formatter. This is always a non-localized pattern. + * May not be null. See class documentation for details. + * + * @serial + */ + private String pattern; + + /** + * The compiled pattern. + */ + transient private char[] compiledPattern; + + private boolean localeInitialized; + + /** + * Tags for the compiled pattern. + */ + private final static int TAG_QUOTE_ASCII_CHAR = 100; + private final static int TAG_QUOTE_CHARS = 101; + + /** + * Locale dependent digit zero. + * + * @see #zeroPaddingNumber + * @see java.text.DecimalFormatSymbols#getZeroDigit + */ + transient private char zeroDigit; + + /** + * The symbols used by this formatter for week names, month names, etc. May not + * be null. + * + * @serial + * @see java.text.DateFormatSymbols + */ + private DateFormatSymbols formatData; + + /** + * We map dates with two-digit years into the century starting at + * defaultCenturyStart, which may be any date. May not be null. + * + * @serial + * @since JDK1.1.4 + */ + private Date defaultCenturyStart; + + transient private int defaultCenturyStartYear; + + private static final int millisPerHour = 60 * 60 * 1000; + private static final int millisPerMinute = 60 * 1000; + + // For time zones that have no names, use strings GMT+minutes and + // GMT-minutes. For instance, in France the time zone is GMT+60. private static final String GMT = "GMT"; - /** - * BH SwingJS switched this to a key with time and date styles. - * - * Cache to hold the DateTimePatterns of a Locale. - */ - private static Hashtable cachedLocaleData - = new Hashtable<>(3); - - /** - * Cache NumberFormat instances with Locale key. - */ - private static Hashtable cachedNumberFormatData - = new Hashtable(3); - - /** - * The Locale used to instantiate this - * SimpleDateFormat. The value may be null if this object - * has been created by an older SimpleDateFormat and - * deserialized. - * - * @serial - * @since 1.6 - */ - private Locale locale; - - /** - * Indicates whether this SimpleDateFormat should use - * the DateFormatSymbols. If true, the format and parse methods - * use the DateFormatSymbols values. If false, the format and - * parse methods call Calendar.getDisplayName or - * Calendar.getDisplayNames. - */ - transient boolean useDateFormatSymbols; - - /** - * Constructs a SimpleDateFormat using the default pattern and - * date format symbols for the default locale. - * Note: This constructor may not support all locales. - * For full coverage, use the factory methods in the {@link DateFormat} - * class. - */ - public SimpleDateFormat() { - this(SHORT, SHORT, Locale.getDefault()); - } + /** + * BH SwingJS switched this to a key with time and date styles. + * + * Cache to hold the DateTimePatterns of a Locale. + */ + private static Hashtable cachedLocaleData; - /** - * Constructs a SimpleDateFormat using the given pattern and - * the default date format symbols for the default locale. - * Note: This constructor may not support all locales. - * For full coverage, use the factory methods in the {@link DateFormat} - * class. - * - * @param pattern the pattern describing the date and time format - * @exception NullPointerException if the given pattern is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - public SimpleDateFormat(String pattern) - { - this(pattern, Locale.getDefault()); - } + /** + * Cache NumberFormat instances with Locale key. + */ + private static Hashtable cachedNumberFormatData; - /** - * Constructs a SimpleDateFormat using the given pattern and - * the default date format symbols for the given locale. - * Note: This constructor may not support all locales. - * For full coverage, use the factory methods in the {@link DateFormat} - * class. - * - * @param pattern the pattern describing the date and time format - * @param locale the locale whose date format symbols should be used - * @exception NullPointerException if the given pattern or locale is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - public SimpleDateFormat(String pattern, Locale locale) - { - if (pattern == null || locale == null) { - throw new NullPointerException(); - } - - initializeCalendar(locale); - this.pattern = pattern; - this.formatData = DateFormatSymbols.getInstance(locale); - this.locale = locale; - initialize(locale); - } + /** + * The Locale used to instantiate this SimpleDateFormat. The value + * may be null if this object has been created by an older + * SimpleDateFormat and deserialized. + * + * @serial + * @since 1.6 + */ + private Locale locale; - /** - * Constructs a SimpleDateFormat using the given pattern and - * date format symbols. - * - * @param pattern the pattern describing the date and time format - * @param formatSymbols the date format symbols to be used for formatting - * @exception NullPointerException if the given pattern or formatSymbols is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) - { - if (pattern == null || formatSymbols == null) { - throw new NullPointerException(); - } - - this.pattern = pattern; - this.formatData = (DateFormatSymbols) formatSymbols.clone(); - this.locale = Locale.getDefault(); - initializeCalendar(this.locale); - initialize(this.locale); - useDateFormatSymbols = true; - } + /** + * Indicates whether this SimpleDateFormat should use the + * DateFormatSymbols. If true, the format and parse methods use the + * DateFormatSymbols values. If false, the format and parse methods call + * Calendar.getDisplayName or Calendar.getDisplayNames. + */ + transient boolean useDateFormatSymbols; + + /** + * Constructs a SimpleDateFormat using the default pattern and date + * format symbols for the default locale. Note: This constructor may not + * support all locales. For full coverage, use the factory methods in the + * {@link DateFormat} class. + */ + public SimpleDateFormat() { + this(SHORT, SHORT, Locale.getDefault()); + } - /* Package-private, called by DateFormat factory methods */ - SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) { - if (loc == null) { - throw new NullPointerException(); - } - - this.locale = loc; - // initialize calendar and related fields - initializeCalendar(loc); - /* try the cache first */ - String key = getKey() + timeStyle + dateStyle; - pattern = cachedLocaleData.get(key); - if (pattern == null) { /* cache miss */ - // BH we just - pattern = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(loc) - .getDateTimePattern(SHORT, SHORT, calendar); - - // BH SwingJS this must be an older style + /** + * Constructs a SimpleDateFormat using the given pattern and the + * default date format symbols for the default locale. Note: This + * constructor may not support all locales. For full coverage, use the factory + * methods in the {@link DateFormat} class. + * + * @param pattern the pattern describing the date and time format + * @exception NullPointerException if the given pattern is null + * @exception IllegalArgumentException if the given pattern is invalid + */ + public SimpleDateFormat(String pattern) { + this(pattern, (Locale) null, true); + } + + /** + * Constructs a SimpleDateFormat using the given pattern and the + * default date format symbols for the given locale. Note: This + * constructor may not support all locales. For full coverage, use the factory + * methods in the {@link DateFormat} class. + * + * @param pattern the pattern describing the date and time format + * @param locale the locale whose date format symbols should be used + * @exception NullPointerException if the given pattern or locale is null + * @exception IllegalArgumentException if the given pattern is invalid + */ + public SimpleDateFormat(String pattern, Locale locale) { + this(pattern, locale, false); + } + + /** + * Constructs a SimpleDateFormat using the given pattern and date + * format symbols. + * + * @param pattern the pattern describing the date and time format + * @param formatSymbols the date format symbols to be used for formatting + * @exception NullPointerException if the given pattern or formatSymbols is + * null + * @exception IllegalArgumentException if the given pattern is invalid + */ + public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) { + if (pattern == null || formatSymbols == null) { + throw new NullPointerException(); + } + + this.pattern = pattern; + this.formatData = (DateFormatSymbols) formatSymbols.clone(); + this.isSimpleMMDDYY = false; + // initializeCalendar(); + // initializeLocale(); + useDateFormatSymbols = true; + } + + /* Package-private, called by DateFormat factory methods */ + SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) { + if (loc == null) { + throw new NullPointerException(); + } + isSimpleMMDDYY = false; + this.locale = loc; + // initialize calendar and related fields + // initializeCalendar(); + /* try the cache first */ + String key = getKey() + timeStyle + dateStyle; + pattern = getCachedLocaleData(key); + if (pattern == null) { /* cache miss */ + // BH we just + pattern = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(loc).getDateTimePattern(SHORT, + SHORT, getCalendar()); + + // BH SwingJS this must be an older style // // ResourceBundle r = LocaleData.getDateFormatData(loc); // if (!isGregorianCalendar()) { @@ -562,9 +547,9 @@ public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) // dateTimePatterns = r.getStringArray("DateTimePatterns"); // } // /* update cache */ - cachedLocaleData.put(key, pattern); - } - formatData = DateFormatSymbols.getInstance(loc); + cachedLocaleData.put(key, pattern); + } +// formatData = DateFormatSymbols.getInstance(loc); // if ((timeStyle >= 0) && (dateStyle >= 0)) { // Object[] dateTimeArgs = {dateTimePatterns[timeStyle], // dateTimePatterns[dateStyle + 4]}; @@ -580,530 +565,644 @@ public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) // throw new IllegalArgumentException("No date or time style specified"); // } // - initialize(loc); - } +// initializeLocale(loc); + } - /* Initialize compiledPattern and numberFormat fields */ - private void initialize(Locale loc) { - // Verify and compile the given pattern. - compiledPattern = compile(pattern); + private static String getCachedLocaleData(String key) { + if (cachedLocaleData == null) { + cachedLocaleData = new Hashtable<>(3); + return null; + } + return cachedLocaleData.get(key); + } - /* try the cache first */ - numberFormat = cachedNumberFormatData.get(loc); - if (numberFormat == null) { /* cache miss */ - numberFormat = NumberFormat.getIntegerInstance(loc); - numberFormat.setGroupingUsed(false); + public SimpleDateFormat(String pattern, Locale locale, boolean allowNull) { + if (pattern == null || !allowNull && locale == null) { + throw new NullPointerException(); + } + this.pattern = pattern; + this.locale = locale; + this.isSimpleMMDDYY = isSimple(pattern, locale); + // lazy calendar initialization for SwingJS + // avoids loading 49 classes! +// this.formatData = DateFormatSymbols.getInstance(locale); + // initializeCalendar(); + // initializeLocale(); + + // TODO Auto-generated constructor stub + } - /* update cache */ - cachedNumberFormatData.put(loc, numberFormat); - } - numberFormat = (NumberFormat) numberFormat.clone(); + /* Initialize compiledPattern and numberFormat fields */ + private void initializeLocale() { + if (locale == null) + locale = Locale.getDefault(); + // Verify and compile the given pattern. + compiledPattern = compile(pattern); - initializeDefaultCentury(); - } + /* try the cache first */ + numberFormat = getCachedNumberFormatData(locale); + if (numberFormat == null) { /* cache miss */ + numberFormat = NumberFormat.getIntegerInstance(locale); + numberFormat.setGroupingUsed(false); - private void initializeCalendar(Locale loc) { - if (calendar == null) { - assert loc != null; - // The format object must be constructed using the symbols for this zone. - // However, the calendar should use the current default TimeZone. - // If this is not contained in the locale zone strings, then the zone - // will be formatted using generic GMT+/-H:MM nomenclature. - calendar = Calendar.getInstance(TimeZone.getDefault(), loc); - } - } + /* update cache */ + cachedNumberFormatData.put(locale, numberFormat); + } + numberFormat = (NumberFormat) numberFormat.clone(); - private String getKey() { - StringBuilder sb = new StringBuilder(); - sb.append(getCalendarName()).append('.'); - sb.append(locale.getLanguage()).append('_').append(locale.getCountry()).append('_').append(locale.getVariant()); - return sb.toString(); - } + } - /** - * Returns the compiled form of the given pattern. The syntax of - * the compiled pattern is: - *
- * CompiledPattern: - * EntryList - * EntryList: - * Entry - * EntryList Entry - * Entry: - * TagField - * TagField data - * TagField: - * Tag Length - * TaggedData - * Tag: - * pattern_char_index - * TAG_QUOTE_CHARS - * Length: - * short_length - * long_length - * TaggedData: - * TAG_QUOTE_ASCII_CHAR ascii_char - * - *
- * - * where `short_length' is an 8-bit unsigned integer between 0 and - * 254. `long_length' is a sequence of an 8-bit integer 255 and a - * 32-bit signed integer value which is split into upper and lower - * 16-bit fields in two char's. `pattern_char_index' is an 8-bit - * integer between 0 and 18. `ascii_char' is an 7-bit ASCII - * character value. `data' depends on its Tag value. - *

- * If Length is short_length, Tag and short_length are packed in a - * single char, as illustrated below. - *

- * char[0] = (Tag << 8) | short_length; - *
- * - * If Length is long_length, Tag and 255 are packed in the first - * char and a 32-bit integer, as illustrated below. - *
- * char[0] = (Tag << 8) | 255; - * char[1] = (char) (long_length >>> 16); - * char[2] = (char) (long_length & 0xffff); - *
- *

- * If Tag is a pattern_char_index, its Length is the number of - * pattern characters. For example, if the given pattern is - * "yyyy", Tag is 1 and Length is 4, followed by no data. - *

- * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's - * following the TagField. For example, if the given pattern is - * "'o''clock'", Length is 7 followed by a char sequence of - * o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k. - *

- * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII - * character in place of Length. For example, if the given pattern - * is "'o'", the TaggedData entry is - * ((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o'). - * - * @exception NullPointerException if the given pattern is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - private char[] compile(String pattern) { - int length = pattern.length(); - boolean inQuote = false; - StringBuilder compiledPattern = new StringBuilder(length * 2); - StringBuilder tmpBuffer = null; - int count = 0; - int lastTag = -1; - - for (int i = 0; i < length; i++) { - char c = pattern.charAt(i); - - if (c == '\'') { - // '' is treated as a single quote regardless of being - // in a quoted section. - if ((i + 1) < length) { - c = pattern.charAt(i + 1); - if (c == '\'') { - i++; - if (count != 0) { - encode(lastTag, count, compiledPattern); - lastTag = -1; - count = 0; - } - if (inQuote) { - tmpBuffer.append(c); - } else { - compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c)); - } - continue; - } - } - if (!inQuote) { - if (count != 0) { - encode(lastTag, count, compiledPattern); - lastTag = -1; - count = 0; - } - if (tmpBuffer == null) { - tmpBuffer = new StringBuilder(length); - } else { - tmpBuffer.setLength(0); - } - inQuote = true; - } else { - int len = tmpBuffer.length(); - if (len == 1) { - char ch = tmpBuffer.charAt(0); - if (ch < 128) { - compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch)); - } else { - compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1)); - compiledPattern.append(ch); - } - } else { - encode(TAG_QUOTE_CHARS, len, compiledPattern); - compiledPattern.append(tmpBuffer); - } - inQuote = false; - } - continue; - } - if (inQuote) { - tmpBuffer.append(c); - continue; - } - if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { - if (count != 0) { - encode(lastTag, count, compiledPattern); - lastTag = -1; - count = 0; - } - if (c < 128) { - // In most cases, c would be a delimiter, such as ':'. - compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c)); - } else { - // Take any contiguous non-ASCII alphabet characters and - // put them in a single TAG_QUOTE_CHARS. - int j; - for (j = i + 1; j < length; j++) { - char d = pattern.charAt(j); - if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) { - break; - } - } - compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i))); - for (; i < j; i++) { - compiledPattern.append(pattern.charAt(i)); - } - i--; - } - continue; - } - - int tag; - if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) { - throw new IllegalArgumentException("Illegal pattern character " + - "'" + c + "'"); - } - if (lastTag == -1 || lastTag == tag) { - lastTag = tag; - count++; - continue; - } - encode(lastTag, count, compiledPattern); - lastTag = tag; - count = 1; - } - - if (inQuote) { - throw new IllegalArgumentException("Unterminated quote"); - } - - if (count != 0) { - encode(lastTag, count, compiledPattern); - } - - // Copy the compiled pattern to a char array - int len = compiledPattern.length(); - char[] r = new char[len]; - compiledPattern.getChars(0, len, r, 0); - return r; - } + private static NumberFormat getCachedNumberFormatData(Locale key) { + if (cachedNumberFormatData == null) { + cachedNumberFormatData = new Hashtable<>(3); + return null; + } + return cachedNumberFormatData.get(key); + } - /** - * Encodes the given tag and length and puts encoded char(s) into buffer. - */ - private static final void encode(int tag, int length, StringBuilder buffer) { - if (length < 255) { - buffer.append((char)(tag << 8 | length)); - } else { - buffer.append((char)((tag << 8) | 0xff)); - buffer.append((char)(length >>> 16)); - buffer.append((char)(length & 0xffff)); - } - } + @Override + public Calendar getCalendar() { + Calendar cal = super.getCalendar(); + if (cal == null) { + initializeCalendar(); + } + return cal; + } - /* Initialize the fields we use to disambiguate ambiguous years. Separate - * so we can call it from readObject(). - */ - private void initializeDefaultCentury() { - calendar.setTime( new Date() ); - calendar.add( Calendar.YEAR, -80 ); - parseAmbiguousDatesAsAfter(calendar.getTime()); - } + private void initializeCalendar() { + if (calendar == null) { + initializeLocale(); + if (formatData == null) + formatData = DateFormatSymbols.getInstance(locale); + // The format object must be constructed using the symbols for this zone. + // However, the calendar should use the current default TimeZone. + // If this is not contained in the locale zone strings, then the zone + // will be formatted using generic GMT+/-H:MM nomenclature. + calendar = Calendar.getInstance(TimeZone.getDefault(), locale); + initializeDefaultCentury(); + } + } - /* Define one-century window into which to disambiguate dates using - * two-digit years. - */ - private void parseAmbiguousDatesAsAfter(Date startDate) { - defaultCenturyStart = startDate; - calendar.setTime(startDate); - defaultCenturyStartYear = calendar.get(Calendar.YEAR); - } + private String getKey() { + StringBuilder sb = new StringBuilder(); + sb.append(getCalendarName()).append('.'); + sb.append(locale.getLanguage()).append('_').append(locale.getCountry()).append('_').append(locale.getVariant()); + return sb.toString(); + } - /** - * Sets the 100-year period 2-digit years will be interpreted as being in - * to begin on the date the user specifies. - * - * @param startDate During parsing, two digit years will be placed in the range - * startDate to startDate + 100 years. - * @see #get2DigitYearStart - * @since 1.2 - */ - public void set2DigitYearStart(Date startDate) { - parseAmbiguousDatesAsAfter(startDate); - } + /** + * Returns the compiled form of the given pattern. The syntax of the compiled + * pattern is:

CompiledPattern: EntryList EntryList: Entry + * EntryList Entry Entry: TagField TagField data TagField: Tag Length TaggedData + * Tag: pattern_char_index TAG_QUOTE_CHARS Length: short_length long_length + * TaggedData: TAG_QUOTE_ASCII_CHAR ascii_char + * + *
+ * + * where `short_length' is an 8-bit unsigned integer between 0 and 254. + * `long_length' is a sequence of an 8-bit integer 255 and a 32-bit signed + * integer value which is split into upper and lower 16-bit fields in two + * char's. `pattern_char_index' is an 8-bit integer between 0 and 18. + * `ascii_char' is an 7-bit ASCII character value. `data' depends on its Tag + * value. + *

+ * If Length is short_length, Tag and short_length are packed in a single char, + * as illustrated below.

char[0] = (Tag << 8) | short_length; + *
+ * + * If Length is long_length, Tag and 255 are packed in the first char and a + * 32-bit integer, as illustrated below.
char[0] = (Tag << 8) | + * 255; char[1] = (char) (long_length >>> 16); char[2] = (char) (long_length & + * 0xffff);
+ *

+ * If Tag is a pattern_char_index, its Length is the number of pattern + * characters. For example, if the given pattern is "yyyy", Tag is 1 and Length + * is 4, followed by no data. + *

+ * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's following the + * TagField. For example, if the given pattern is "'o''clock'", Length is 7 + * followed by a char sequence of + * o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k. + *

+ * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII character in place of + * Length. For example, if the given pattern is "'o'", the TaggedData entry is + * ((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o'). + * + * @exception NullPointerException if the given pattern is null + * @exception IllegalArgumentException if the given pattern is invalid + */ + private char[] compile(String pattern) { + int length = pattern.length(); + boolean inQuote = false; + StringBuilder compiledPattern = new StringBuilder(length * 2); + StringBuilder tmpBuffer = null; + int count = 0; + int lastTag = -1; + + for (int i = 0; i < length; i++) { + char c = pattern.charAt(i); + + if (c == '\'') { + // '' is treated as a single quote regardless of being + // in a quoted section. + if ((i + 1) < length) { + c = pattern.charAt(i + 1); + if (c == '\'') { + i++; + if (count != 0) { + encode(lastTag, count, compiledPattern); + lastTag = -1; + count = 0; + } + if (inQuote) { + tmpBuffer.append(c); + } else { + compiledPattern.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c)); + } + continue; + } + } + if (!inQuote) { + if (count != 0) { + encode(lastTag, count, compiledPattern); + lastTag = -1; + count = 0; + } + if (tmpBuffer == null) { + tmpBuffer = new StringBuilder(length); + } else { + tmpBuffer.setLength(0); + } + inQuote = true; + } else { + int len = tmpBuffer.length(); + if (len == 1) { + char ch = tmpBuffer.charAt(0); + if (ch < 128) { + compiledPattern.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | ch)); + } else { + compiledPattern.append((char) (TAG_QUOTE_CHARS << 8 | 1)); + compiledPattern.append(ch); + } + } else { + encode(TAG_QUOTE_CHARS, len, compiledPattern); + compiledPattern.append(tmpBuffer); + } + inQuote = false; + } + continue; + } + if (inQuote) { + tmpBuffer.append(c); + continue; + } + if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) { + if (count != 0) { + encode(lastTag, count, compiledPattern); + lastTag = -1; + count = 0; + } + if (c < 128) { + // In most cases, c would be a delimiter, such as ':'. + compiledPattern.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c)); + } else { + // Take any contiguous non-ASCII alphabet characters and + // put them in a single TAG_QUOTE_CHARS. + int j; + for (j = i + 1; j < length; j++) { + char d = pattern.charAt(j); + if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) { + break; + } + } + compiledPattern.append((char) (TAG_QUOTE_CHARS << 8 | (j - i))); + for (; i < j; i++) { + compiledPattern.append(pattern.charAt(i)); + } + i--; + } + continue; + } + + int tag; + if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) { + throw new IllegalArgumentException("Illegal pattern character " + "'" + c + "'"); + } + if (lastTag == -1 || lastTag == tag) { + lastTag = tag; + count++; + continue; + } + encode(lastTag, count, compiledPattern); + lastTag = tag; + count = 1; + } + + if (inQuote) { + throw new IllegalArgumentException("Unterminated quote"); + } + + if (count != 0) { + encode(lastTag, count, compiledPattern); + } + + // Copy the compiled pattern to a char array + int len = compiledPattern.length(); + char[] r = new char[len]; + compiledPattern.getChars(0, len, r, 0); + return r; + } - /** - * Returns the beginning date of the 100-year period 2-digit years are interpreted - * as being within. - * - * @return the start of the 100-year period into which two digit years are - * parsed - * @see #set2DigitYearStart - * @since 1.2 - */ - public Date get2DigitYearStart() { - return defaultCenturyStart; - } + /** + * Encodes the given tag and length and puts encoded char(s) into buffer. + */ + private static final void encode(int tag, int length, StringBuilder buffer) { + if (length < 255) { + buffer.append((char) (tag << 8 | length)); + } else { + buffer.append((char) ((tag << 8) | 0xff)); + buffer.append((char) (length >>> 16)); + buffer.append((char) (length & 0xffff)); + } + } - /** - * Formats the given Date into a date/time string and appends - * the result to the given StringBuffer. - * - * @param date the date-time value to be formatted into a date-time string. - * @param toAppendTo where the new date-time text is to be appended. - * @param pos the formatting position. On input: an alignment field, - * if desired. On output: the offsets of the alignment field. - * @return the formatted date-time string. - * @exception NullPointerException if the given date is null - */ - @Override - public StringBuffer format(Date date, StringBuffer toAppendTo, - FieldPosition pos) - { - pos.beginIndex = pos.endIndex = 0; - return format(date, toAppendTo, pos.getFieldDelegate()); - } + /* + * Initialize the fields we use to disambiguate ambiguous years. Separate so we + * can call it from readObject(). + */ + private void initializeDefaultCentury() { + calendar.setTime(new Date()); + calendar.add(Calendar.YEAR, -80); + parseAmbiguousDatesAsAfter(calendar.getTime()); + } - // Called from Format after creating a FieldDelegate - private StringBuffer format(Date date, StringBuffer toAppendTo, - FieldDelegate delegate) { - // Convert input date to time field list - calendar.setTime(date); - - boolean useDateFormatSymbols = useDateFormatSymbols(); - - for (int i = 0; i < compiledPattern.length; ) { - int tag = compiledPattern[i] >>> 8; - int count = compiledPattern[i++] & 0xff; - if (count == 255) { - count = compiledPattern[i++] << 16; - count |= compiledPattern[i++]; - } - - switch (tag) { - case TAG_QUOTE_ASCII_CHAR: - toAppendTo.append((char)count); - break; - - case TAG_QUOTE_CHARS: - toAppendTo.append(compiledPattern, i, count); - i += count; - break; - - default: - subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); - break; - } - } - return toAppendTo; - } + /* + * Define one-century window into which to disambiguate dates using two-digit + * years. + */ + private void parseAmbiguousDatesAsAfter(Date startDate) { + defaultCenturyStart = startDate; + getCalendar().setTime(startDate); + defaultCenturyStartYear = calendar.get(Calendar.YEAR); + } + + /** + * Sets the 100-year period 2-digit years will be interpreted as being in to + * begin on the date the user specifies. + * + * @param startDate During parsing, two digit years will be placed in the range + * startDate to + * startDate + 100 years. + * @see #get2DigitYearStart + * @since 1.2 + */ + public void set2DigitYearStart(Date startDate) { + parseAmbiguousDatesAsAfter(startDate); + } + + /** + * Returns the beginning date of the 100-year period 2-digit years are + * interpreted as being within. + * + * @return the start of the 100-year period into which two digit years are + * parsed + * @see #set2DigitYearStart + * @since 1.2 + */ + public Date get2DigitYearStart() { + return defaultCenturyStart; + } + + /** + * Formats the given Date into a date/time string and appends the + * result to the given StringBuffer. + * + * @param date the date-time value to be formatted into a date-time + * string. + * @param toAppendTo where the new date-time text is to be appended. + * @param pos the formatting position. On input: an alignment field, if + * desired. On output: the offsets of the alignment field. + * @return the formatted date-time string. + * @exception NullPointerException if the given date is null + */ + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { + if (pos == null) { + return (StringBuffer) formatSimpleJS(date, toAppendTo); + } + pos.beginIndex = pos.endIndex = 0; + return format(date, toAppendTo, pos.getFieldDelegate()); + } - /** - * Formats an Object producing an AttributedCharacterIterator. - * You can use the returned AttributedCharacterIterator - * to build the resulting String, as well as to determine information - * about the resulting String. - *

- * Each attribute key of the AttributedCharacterIterator will be of type - * DateFormat.Field, with the corresponding attribute value - * being the same as the attribute key. - * - * @exception NullPointerException if obj is null. - * @exception IllegalArgumentException if the Format cannot format the - * given object, or if the Format's pattern string is invalid. - * @param obj The object to format - * @return AttributedCharacterIterator describing the formatted value. - * @since 1.4 - */ @Override - public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - StringBuffer sb = new StringBuffer(); - CharacterIteratorFieldDelegate delegate = new - CharacterIteratorFieldDelegate(); - - if (obj instanceof Date) { - format((Date)obj, sb, delegate); - } - else if (obj instanceof Number) { - format(new Date(((Number)obj).longValue()), sb, delegate); - } - else if (obj == null) { - throw new NullPointerException( - "formatToCharacterIterator must be passed non-null object"); - } - else { - throw new IllegalArgumentException( - "Cannot format given Object as a Date"); - } - return delegate.getIterator(sb.toString()); + public final String format (Object obj) { + return format(obj, new StringBuffer(), isSimpleMMDDYY ? null : new FieldPosition(0)).toString(); } + + @Override + public String format(Date date) { + if (isSimpleMMDDYY) { + return (String) formatSimpleJS(date, null); + } + return super.format(date); + } + + private boolean isSimple(String pattern, Locale locale) { + if ((/** @j2sNative false && */ + true)) + return false; + // JavaScript only ignoring Buddhist and Japanese for simple here +// if (locale.getLanguage() == "th" && locale.getCountry() == "TH" +// ||locale.getVariant() == "JP" && locale.getLanguage() == "ja" +// && locale.getCountry() == "JP") { +// return false; +// } + + for (int i = pattern.length(); --i >= 0;) { + char ch = pattern.charAt(i); + switch (ch) { + case 'M': + case 'd': + case 'H': // H Hour in day (0-23) + case 'k': // k Hour in day (1-24) + case 'y': + case 'h': + case 'm': + case 's': + break; + default: + if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') + return false; + } + } + return true; + } + + // toISOString() returns a string in the + // 1970-01-01T00:00:00.000Z + // 0.........1......... + // 01234567890123456789 + // + //new Date().toString() + // Sun Mar 09 14:35:53 GMT-0500 2025" + // 0.........1......... + // 01234567890123456789 + private final static String[] fields = { "yyyy", "yy", "MM", "M", "dd", "d", "hh", "h", "HH", "H", "kk", "k", "mm", "m", "ss", "s" }; + private final static int[] fieldlens = { 4, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 }; + private final static int[] fieldzero = { 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }; + private final static int[] adj = { 0, 0, 3, 3, 0, 0, 12, 12, 23, 23, 24, 24, 0, 0, 0, 0 }; + private final static int[] isopts = { 0, 2, 5, 5, 8, 8, 11, 11, 11, 11, 11, 11, 14, 14, 17, 17 }; + private final static int[] isolens = { 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + private final static int[] locpts = { -4, -2, 4, 4, 8, 8, 11, 11, 11, 11, 11, 11, 14, 14, 17, 17 }; + private final static int[] loclens = { 4, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + private final static int fieldCount = fields.length; + + private Object formatSimpleJS(Date date, StringBuffer sb) { + // considered doing this -- still could if we want GMT + //String iso = (/** @j2sNative date.toISOString() || */""); + String loc = (/** @j2sNative date.toString() || */""); + String ret = pattern; + for (int i = 0, l = 2; i < fieldCount; i++, l = 3 - l) { + int pt = ret.indexOf(fields[i]); + if (pt >= 0) { + ret = rep(ret, pt, fieldlens[i], fieldzero[i], loc, locpts[i], adj[i], loclens[i]); + } + } + if (sb == null) + return ret; + sb.append(ret); + return sb; + } - // Map index into pattern character string to Calendar field number - private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = - { - Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE, - Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE, - Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK, - Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH, - Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, - Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET, - Calendar.ZONE_OFFSET - }; - - // Map index into pattern character string to DateFormat field number - private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = { - DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD, - DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD, - DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD, - DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD, - DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD, - DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD, - DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD, - DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD, - DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD, - }; - - // Maps from DecimalFormatSymbols index to Field constant - private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = { - Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH, - Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE, - Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK, - Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH, - Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH, - Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE, - Field.TIME_ZONE, - }; - - /** - * Private member function that does the real date/time formatting. - */ - private void subFormat(int patternCharIndex, int count, - FieldDelegate delegate, StringBuffer buffer, - boolean useDateFormatSymbols) - { - int maxIntCount = Integer.MAX_VALUE; - String current = null; - int beginOffset = buffer.length(); - - int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; - int value = calendar.get(field); - int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; - if (!useDateFormatSymbols) { - current = calendar.getDisplayName(field, style, locale); - } - - // Note: zeroPaddingNumber() assumes that maxDigits is either - // 2 or maxIntCount. If we make any changes to this, - // zeroPaddingNumber() must be fixed. - - switch (patternCharIndex) { - case 0: // 'G' - ERA - if (useDateFormatSymbols) { - String[] eras = formatData.getEras(); - if (value < eras.length) - current = eras[value]; - } - if (current == null) - current = ""; - break; - - case 1: // 'y' - YEAR - if (calendar instanceof GregorianCalendar) { - if (count >= 4) - zeroPaddingNumber(value, count, maxIntCount, buffer); - else // count < 4 - zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96 - } else { - if (current == null) { - zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count, - maxIntCount, buffer); - } - } - break; - - case 2: // 'M' - MONTH - if (useDateFormatSymbols) { - String[] months; - if (count >= 4) { - months = formatData.getMonths(); - current = months[value]; - } else if (count == 3) { - months = formatData.getShortMonths(); - current = months[value]; - } - } else { - if (count < 3) { - current = null; - } - } - if (current == null) { - zeroPaddingNumber(value+1, count, maxIntCount, buffer); - } - break; - - case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 - if (current == null) { - if (value == 0) - zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1, - count, maxIntCount, buffer); - else - zeroPaddingNumber(value, count, maxIntCount, buffer); - } - break; - - case 9: // 'E' - DAY_OF_WEEK - if (useDateFormatSymbols) { - String[] weekdays; - if (count >= 4) { - weekdays = formatData.getWeekdays(); - current = weekdays[value]; - } else { // count < 4, use abbreviated form if exists - weekdays = formatData.getShortWeekdays(); - current = weekdays[value]; - } - } - break; - - case 14: // 'a' - AM_PM - if (useDateFormatSymbols) { - String[] ampm = formatData.getAmPmStrings(); - current = ampm[value]; - } - break; - - case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM - if (current == null) { - if (value == 0) - zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1, - count, maxIntCount, buffer); - else - zeroPaddingNumber(value, count, maxIntCount, buffer); - } - break; - - case 17: // 'z' - ZONE_OFFSET - if (current == null) { - String id = calendar.getTimeZone().getID(); - // GMT-00;00 + private final String months = "JanFebMarAprMayJunJulAugSepOctNovDec"; + + private String rep(String p, int pt, int flen, int fzero, String iso, int i0, int isoadj, int isolen) { + String v = (i0 < 0 ? iso.substring(iso.length() + i0) : iso.substring(i0, i0 + isolen)); + int iv = -1; + if (isoadj == 3) { + iv = (months.indexOf(v)/3 + 1); + } else if (isoadj != 0) { + iv = Integer.parseInt(v); + switch (isoadj) { + case 12: + if (iv > 12) + iv -= 12; + case 24: + if (iv == 0) + iv = 24; + } + } + if (isoadj > 0) { + v = (fzero == 0 && iv < 10 ? "0" : "") + iv; + } else if (fzero > 0 && v.charAt(0) == '0') { + v = v.substring(1); + } + return p.substring(0, pt) + v + p.substring(pt + flen); + } + + // Called from Format after creating a FieldDelegate + private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { + // Convert input date to time field list + getCalendar().setTime(date); + + boolean useDateFormatSymbols = useDateFormatSymbols(); + + for (int i = 0; i < compiledPattern.length;) { + int tag = compiledPattern[i] >>> 8; + int count = compiledPattern[i++] & 0xff; + if (count == 255) { + count = compiledPattern[i++] << 16; + count |= compiledPattern[i++]; + } + + switch (tag) { + case TAG_QUOTE_ASCII_CHAR: + toAppendTo.append((char) count); + break; + + case TAG_QUOTE_CHARS: + toAppendTo.append(compiledPattern, i, count); + i += count; + break; + + default: + subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); + break; + } + } + return toAppendTo; + } + + /** + * Formats an Object producing an AttributedCharacterIterator. You + * can use the returned AttributedCharacterIterator to build the + * resulting String, as well as to determine information about the resulting + * String. + *

+ * Each attribute key of the AttributedCharacterIterator will be of type + * DateFormat.Field, with the corresponding attribute value being + * the same as the attribute key. + * + * @exception NullPointerException if obj is null. + * @exception IllegalArgumentException if the Format cannot format the given + * object, or if the Format's pattern string + * is invalid. + * @param obj The object to format + * @return AttributedCharacterIterator describing the formatted value. + * @since 1.4 + */ + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object obj) { + StringBuffer sb = new StringBuffer(); + CharacterIteratorFieldDelegate delegate = new CharacterIteratorFieldDelegate(); + + if (obj instanceof Date) { + format((Date) obj, sb, delegate); + } else if (obj instanceof Number) { + format(new Date(((Number) obj).longValue()), sb, delegate); + } else if (obj == null) { + throw new NullPointerException("formatToCharacterIterator must be passed non-null object"); + } else { + throw new IllegalArgumentException("Cannot format given Object as a Date"); + } + return delegate.getIterator(sb.toString()); + } + + // Map index into pattern character string to Calendar field number + private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = { Calendar.ERA, Calendar.YEAR, Calendar.MONTH, + Calendar.DATE, Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, + Calendar.MILLISECOND, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH, + Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, + Calendar.ZONE_OFFSET, Calendar.ZONE_OFFSET }; + + // Map index into pattern character string to DateFormat field number + private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = { DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, + DateFormat.MONTH_FIELD, DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD, DateFormat.HOUR_OF_DAY0_FIELD, + DateFormat.MINUTE_FIELD, DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD, + DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD, DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, + DateFormat.WEEK_OF_YEAR_FIELD, DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD, + DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD, DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD, }; + + // Maps from DecimalFormatSymbols index to Field constant + private static Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID; + + private static Field[] getPatternIndexFieldIDs() { + if (PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID == null) { + PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = new Field[] { Field.ERA, Field.YEAR, Field.MONTH, + Field.DAY_OF_MONTH, Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE, Field.SECOND, Field.MILLISECOND, + Field.DAY_OF_WEEK, Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH, Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH, + Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE, Field.TIME_ZONE, }; + } + return PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID; + + } + /** + * Private member function that does the real date/time formatting. + */ + private void subFormat(int patternCharIndex, int count, FieldDelegate delegate, StringBuffer buffer, + boolean useDateFormatSymbols) { + int maxIntCount = Integer.MAX_VALUE; + String current = null; + int beginOffset = buffer.length(); + + int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; + // getCalendar() initializes the calendar + int value = getCalendar().get(field); + int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; + if (!useDateFormatSymbols) { + current = getCalendar().getDisplayName(field, style, locale); + } + + // Note: zeroPaddingNumber() assumes that maxDigits is either + // 2 or maxIntCount. If we make any changes to this, + // zeroPaddingNumber() must be fixed. + + switch (patternCharIndex) { + case 0: // 'G' - ERA + if (useDateFormatSymbols) { + String[] eras = formatData.getEras(); + if (value < eras.length) + current = eras[value]; + } + if (current == null) + current = ""; + break; + + case 1: // 'y' - YEAR + if (calendar instanceof GregorianCalendar) { + if (count >= 4) + zeroPaddingNumber(value, count, maxIntCount, buffer); + else // count < 4 + zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96 + } else { + if (current == null) { + zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count, maxIntCount, buffer); + } + } + break; + + case 2: // 'M' - MONTH + if (useDateFormatSymbols) { + String[] months; + if (count >= 4) { + months = formatData.getMonths(); + current = months[value]; + } else if (count == 3) { + months = formatData.getShortMonths(); + current = months[value]; + } + } else { + if (count < 3) { + current = null; + } + } + if (current == null) { + zeroPaddingNumber(value + 1, count, maxIntCount, buffer); + } + break; + + case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 + if (current == null) { + if (value == 0) + zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1, count, maxIntCount, buffer); + else + zeroPaddingNumber(value, count, maxIntCount, buffer); + } + break; + + case 9: // 'E' - DAY_OF_WEEK + if (useDateFormatSymbols) { + String[] weekdays; + if (count >= 4) { + weekdays = formatData.getWeekdays(); + current = weekdays[value]; + } else { // count < 4, use abbreviated form if exists + weekdays = formatData.getShortWeekdays(); + current = weekdays[value]; + } + } + break; + + case 14: // 'a' - AM_PM + if (useDateFormatSymbols) { + String[] ampm = formatData.getAmPmStrings(); + current = ampm[value]; + } + break; + + case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM + if (current == null) { + if (value == 0) + zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR) + 1, count, maxIntCount, buffer); + else + zeroPaddingNumber(value, count, maxIntCount, buffer); + } + break; + + case 17: // 'z' - ZONE_OFFSET + if (current == null) { + String id = calendar.getTimeZone().getID(); + // GMT-00;00 // switch (count) { // case 1: // // just send ID @@ -1113,7 +1212,7 @@ private void subFormat(int patternCharIndex, int count, // case 3: // case 4: // } - buffer.append(id); + buffer.append(id); // //// if (formatData.locale == null || formatData.isZoneStringsSet) { @@ -1138,100 +1237,97 @@ private void subFormat(int patternCharIndex, int count, //// int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG); //// buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale)); //// } - } - break; - - case 18: // 'Z' - ZONE_OFFSET ("-/+hhmm" form) - value = (calendar.get(Calendar.ZONE_OFFSET) + - calendar.get(Calendar.DST_OFFSET)) / 60000; - - int width = 4; - if (value >= 0) { - buffer.append('+'); - } else { - width++; - } - - int num = (value / 60) * 100 + (value % 60); - CalendarUtils.sprintf0d(buffer, num, width); - break; - - default: - // case 3: // 'd' - DATE - // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 - // case 6: // 'm' - MINUTE - // case 7: // 's' - SECOND - // case 8: // 'S' - MILLISECOND - // case 10: // 'D' - DAY_OF_YEAR - // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH - // case 12: // 'w' - WEEK_OF_YEAR - // case 13: // 'W' - WEEK_OF_MONTH - // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM - if (current == null) { - zeroPaddingNumber(value, count, maxIntCount, buffer); - } - break; - } // switch (patternCharIndex) - - if (current != null) { - buffer.append(current); - } - - int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex]; - Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex]; - - delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer); - } - - /** - * Formats a number with the specified minimum and maximum number of digits. - */ - private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer) - { - // Optimization for 1, 2 and 4 digit numbers. This should - // cover most cases of formatting date/time related items. - // Note: This optimization code assumes that maxDigits is - // either 2 or Integer.MAX_VALUE (maxIntCount in format()). - try { - if (zeroDigit == 0) { - zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit(); - } - if (value >= 0) { - if (value < 100 && minDigits >= 1 && minDigits <= 2) { - if (value < 10) { - if (minDigits == 2) { - buffer.append(zeroDigit); - } - buffer.append((char)(zeroDigit + value)); - } else { - buffer.append((char)(zeroDigit + value / 10)); - buffer.append((char)(zeroDigit + value % 10)); - } - return; - } else if (value >= 1000 && value < 10000) { - if (minDigits == 4) { - buffer.append((char)(zeroDigit + value / 1000)); - value %= 1000; - buffer.append((char)(zeroDigit + value / 100)); - value %= 100; - buffer.append((char)(zeroDigit + value / 10)); - buffer.append((char)(zeroDigit + value % 10)); - return; - } - if (minDigits == 2 && maxDigits == 2) { - zeroPaddingNumber(value % 100, 2, 2, buffer); - return; - } - } - } - } catch (Exception e) { - } - - numberFormat.setMinimumIntegerDigits(minDigits); - numberFormat.setMaximumIntegerDigits(maxDigits); - numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE); - } + } + break; + + case 18: // 'Z' - ZONE_OFFSET ("-/+hhmm" form) + value = (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / 60000; + + int width = 4; + if (value >= 0) { + buffer.append('+'); + } else { + width++; + } + + int num = (value / 60) * 100 + (value % 60); + CalendarUtils.sprintf0d(buffer, num, width); + break; + + default: + // case 3: // 'd' - DATE + // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 + // case 6: // 'm' - MINUTE + // case 7: // 's' - SECOND + // case 8: // 'S' - MILLISECOND + // case 10: // 'D' - DAY_OF_YEAR + // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH + // case 12: // 'w' - WEEK_OF_YEAR + // case 13: // 'W' - WEEK_OF_MONTH + // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM + if (current == null) { + zeroPaddingNumber(value, count, maxIntCount, buffer); + } + break; + } // switch (patternCharIndex) + + if (current != null) { + buffer.append(current); + } + + int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex]; + Field f = getPatternIndexFieldIDs()[patternCharIndex]; + + delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer); + } + /** + * Formats a number with the specified minimum and maximum number of digits. + */ + private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer) { + // Optimization for 1, 2 and 4 digit numbers. This should + // cover most cases of formatting date/time related items. + // Note: This optimization code assumes that maxDigits is + // either 2 or Integer.MAX_VALUE (maxIntCount in format()). + try { + if (zeroDigit == 0) { + zeroDigit = ((DecimalFormat) numberFormat).getDecimalFormatSymbols().getZeroDigit(); + } + if (value >= 0) { + if (value < 100 && minDigits >= 1 && minDigits <= 2) { + if (value < 10) { + if (minDigits == 2) { + buffer.append(zeroDigit); + } + buffer.append((char) (zeroDigit + value)); + } else { + buffer.append((char) (zeroDigit + value / 10)); + buffer.append((char) (zeroDigit + value % 10)); + } + return; + } else if (value >= 1000 && value < 10000) { + if (minDigits == 4) { + buffer.append((char) (zeroDigit + value / 1000)); + value %= 1000; + buffer.append((char) (zeroDigit + value / 100)); + value %= 100; + buffer.append((char) (zeroDigit + value / 10)); + buffer.append((char) (zeroDigit + value % 10)); + return; + } + if (minDigits == 2 && maxDigits == 2) { + zeroPaddingNumber(value % 100, 2, 2, buffer); + return; + } + } + } + } catch (Exception e) { + } + + numberFormat.setMinimumIntegerDigits(minDigits); + numberFormat.setMaximumIntegerDigits(maxDigits); + numberFormat.format((long) value, buffer, DontCareFieldPosition.INSTANCE); + } /** * Parses text from a string to produce a Date. @@ -1280,881 +1376,841 @@ public Date parse(String text, ParsePosition pos) { // } // pos.index = i0; // return null; - int start = pos.index; - int oldStart = start; - int textLength = text.length(); - - calendar.clear(); // Clears all the time fields - - boolean[] ambiguousYear = {false}; - - - for (int i = 0; i < compiledPattern.length; ) { - int tag = compiledPattern[i] >>> 8; - int count = compiledPattern[i++] & 0xff; - if (count == 255) { - count = compiledPattern[i++] << 16; - count |= compiledPattern[i++]; - } - - switch (tag) { - case TAG_QUOTE_ASCII_CHAR: - if (start >= textLength || text.charAt(start) != (char)count) { - pos.index = oldStart; - pos.errorIndex = start; - return null; - } - start++; - break; - - case TAG_QUOTE_CHARS: - while (count-- > 0) { - if (start >= textLength || text.charAt(start) != compiledPattern[i++]) { - pos.index = oldStart; - pos.errorIndex = start; - return null; - } - start++; - } - break; - - default: - // Peek the next pattern to determine if we need to - // obey the number of pattern letters for - // parsing. It's required when parsing contiguous - // digit text (e.g., "20010704") with a pattern which - // has no delimiters between fields, like "yyyyMMdd". - boolean obeyCount = false; - if (i < compiledPattern.length) { - int nextTag = compiledPattern[i] >>> 8; - if (!(nextTag == TAG_QUOTE_ASCII_CHAR || nextTag == TAG_QUOTE_CHARS)) { - obeyCount = true; - } - } - start = subParse(text, start, tag, count, obeyCount, - ambiguousYear, pos); - if (start < 0) { - pos.index = oldStart; - return null; - } - } - } - - // At this point the fields of Calendar have been set. Calendar - // will fill in default values for missing fields when the time - // is computed. - - pos.index = start; - - // This part is a problem: When we call parsedDate.after, we compute the time. - // Take the date April 3 2004 at 2:30 am. When this is first set up, the year - // will be wrong if we're parsing a 2-digit year pattern. It will be 1904. - // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am - // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am - // on that day. It is therefore parsed out to fields as 3:30 am. Then we - // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is - // a Saturday, so it can have a 2:30 am -- and it should. [LIU] + int start = pos.index; + int oldStart = start; + int textLength = text.length(); + + getCalendar().clear(); // Clears all the time fields + + boolean[] ambiguousYear = { false }; + + for (int i = 0; i < compiledPattern.length;) { + int tag = compiledPattern[i] >>> 8; + int count = compiledPattern[i++] & 0xff; + if (count == 255) { + count = compiledPattern[i++] << 16; + count |= compiledPattern[i++]; + } + + switch (tag) { + case TAG_QUOTE_ASCII_CHAR: + if (start >= textLength || text.charAt(start) != (char) count) { + pos.index = oldStart; + pos.errorIndex = start; + return null; + } + start++; + break; + + case TAG_QUOTE_CHARS: + while (count-- > 0) { + if (start >= textLength || text.charAt(start) != compiledPattern[i++]) { + pos.index = oldStart; + pos.errorIndex = start; + return null; + } + start++; + } + break; + + default: + // Peek the next pattern to determine if we need to + // obey the number of pattern letters for + // parsing. It's required when parsing contiguous + // digit text (e.g., "20010704") with a pattern which + // has no delimiters between fields, like "yyyyMMdd". + boolean obeyCount = false; + if (i < compiledPattern.length) { + int nextTag = compiledPattern[i] >>> 8; + if (!(nextTag == TAG_QUOTE_ASCII_CHAR || nextTag == TAG_QUOTE_CHARS)) { + obeyCount = true; + } + } + start = subParse(text, start, tag, count, obeyCount, ambiguousYear, pos); + if (start < 0) { + pos.index = oldStart; + return null; + } + } + } + + // At this point the fields of Calendar have been set. Calendar + // will fill in default values for missing fields when the time + // is computed. + + pos.index = start; + + // This part is a problem: When we call parsedDate.after, we compute the time. + // Take the date April 3 2004 at 2:30 am. When this is first set up, the year + // will be wrong if we're parsing a 2-digit year pattern. It will be 1904. + // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am + // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am + // on that day. It is therefore parsed out to fields as 3:30 am. Then we + // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is + // a Saturday, so it can have a 2:30 am -- and it should. [LIU] /* * Date parsedDate = calendar.getTime(); if( ambiguousYear[0] && * !parsedDate.after(defaultCenturyStart) ) { calendar.add(Calendar.YEAR, 100); * parsedDate = calendar.getTime(); } */ - // Because of the above condition, save off the fields in case we need to readjust. - // The procedure we use here is not particularly efficient, but there is no other - // way to do this given the API restrictions present in Calendar. We minimize - // inefficiency by only performing this computation when it might apply, that is, - // when the two-digit year is equal to the start year, and thus might fall at the - // front or the back of the default century. This only works because we adjust - // the year correctly to start with in other cases -- see subParse(). - Date parsedDate; - try { - if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year - { - // We need a copy of the fields, and we need to avoid triggering a call to - // complete(), which will recalculate the fields. Since we can't access - // the fields[] array in Calendar, we clone the entire object. This will - // stop working if Calendar.clone() is ever rewritten to call complete(). - Calendar savedCalendar = (Calendar)calendar.clone(); - parsedDate = calendar.getTime(); - if (parsedDate.before(defaultCenturyStart)) - { - // We can't use add here because that does a complete() first. - savedCalendar.set(Calendar.YEAR, defaultCenturyStartYear + 100); - parsedDate = savedCalendar.getTime(); - } - } - else parsedDate = calendar.getTime(); - } - // An IllegalArgumentException will be thrown by Calendar.getTime() - // if any fields are out of range, e.g., MONTH == 17. - catch (IllegalArgumentException e) { - pos.errorIndex = start; - pos.index = oldStart; - return null; - } - - return parsedDate; + // Because of the above condition, save off the fields in case we need to + // readjust. + // The procedure we use here is not particularly efficient, but there is no + // other + // way to do this given the API restrictions present in Calendar. We minimize + // inefficiency by only performing this computation when it might apply, that + // is, + // when the two-digit year is equal to the start year, and thus might fall at + // the + // front or the back of the default century. This only works because we adjust + // the year correctly to start with in other cases -- see subParse(). + Date parsedDate; + try { + if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year + { + // We need a copy of the fields, and we need to avoid triggering a call to + // complete(), which will recalculate the fields. Since we can't access + // the fields[] array in Calendar, we clone the entire object. This will + // stop working if Calendar.clone() is ever rewritten to call complete(). + Calendar savedCalendar = (Calendar) getCalendar().clone(); + parsedDate = calendar.getTime(); + if (parsedDate.before(defaultCenturyStart)) { + // We can't use add here because that does a complete() first. + savedCalendar.set(Calendar.YEAR, defaultCenturyStartYear + 100); + parsedDate = savedCalendar.getTime(); + } + } else + parsedDate = calendar.getTime(); + } + // An IllegalArgumentException will be thrown by Calendar.getTime() + // if any fields are out of range, e.g., MONTH == 17. + catch (IllegalArgumentException e) { + pos.errorIndex = start; + pos.index = oldStart; + return null; + } + + return parsedDate; } - /** - * Private code-size reduction function used by subParse. - * @param text the time text being parsed. - * @param start where to start parsing. - * @param field the date field being parsed. - * @param data the string array to parsed. - * @return the new start position if matching succeeded; a negative number - * indicating matching failure, otherwise. - */ - private int matchString(String text, int start, int field, String[] data) - { - int i = 0; - int count = data.length; - - if (field == Calendar.DAY_OF_WEEK) i = 1; - - // There may be multiple strings in the data[] array which begin with - // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). - // We keep track of the longest match, and return that. Note that this - // unfortunately requires us to test all array elements. - int bestMatchLength = 0, bestMatch = -1; - for (; i bestMatchLength && - text.regionMatches(true, start, data[i], 0, length)) - { - bestMatch = i; - bestMatchLength = length; - } - } - if (bestMatch >= 0) - { - calendar.set(field, bestMatch); - return start + bestMatchLength; - } - return -start; - } + /** + * Private code-size reduction function used by subParse. + * + * @param text the time text being parsed. + * @param start where to start parsing. + * @param field the date field being parsed. + * @param data the string array to parsed. + * @return the new start position if matching succeeded; a negative number + * indicating matching failure, otherwise. + */ + private int matchString(String text, int start, int field, String[] data) { + int i = 0; + int count = data.length; + + if (field == Calendar.DAY_OF_WEEK) + i = 1; + + // There may be multiple strings in the data[] array which begin with + // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech). + // We keep track of the longest match, and return that. Note that this + // unfortunately requires us to test all array elements. + int bestMatchLength = 0, bestMatch = -1; + for (; i < count; ++i) { + int length = data[i].length(); + // Always compare if we have no match yet; otherwise only compare + // against potentially better matches (longer strings). + if (length > bestMatchLength && text.regionMatches(true, start, data[i], 0, length)) { + bestMatch = i; + bestMatchLength = length; + } + } + if (bestMatch >= 0) { + calendar.set(field, bestMatch); + return start + bestMatchLength; + } + return -start; + } - /** - * Performs the same thing as matchString(String, int, int, - * String[]). This method takes a Map instead of - * String[]. - */ - private int matchString(String text, int start, int field, Map data) { - if (data != null) { - String bestMatch = null; - - for (String name : data.keySet()) { - int length = name.length(); - if (bestMatch == null || length > bestMatch.length()) { - if (text.regionMatches(true, start, name, 0, length)) { - bestMatch = name; - } - } - } - - if (bestMatch != null) { - calendar.set(field, data.get(bestMatch)); - return start + bestMatch.length(); - } - } - return -start; - } + /** + * Performs the same thing as matchString(String, int, int, String[]). This + * method takes a Map instead of String[]. + */ + private int matchString(String text, int start, int field, Map data) { + if (data != null) { + String bestMatch = null; + + for (String name : data.keySet()) { + int length = name.length(); + if (bestMatch == null || length > bestMatch.length()) { + if (text.regionMatches(true, start, name, 0, length)) { + bestMatch = name; + } + } + } + + if (bestMatch != null) { + calendar.set(field, data.get(bestMatch)); + return start + bestMatch.length(); + } + } + return -start; + } - private int matchZoneString(String text, int start, int zoneIndex) { - for (int j = 1; j <= 4; ++j) { - // Checking long and short zones [1 & 2], - // and long and short daylight [3 & 4]. - String[][] zoneStrings = formatData.getZoneStringsWrapper(); - String zoneName = zoneStrings[zoneIndex][j]; - if (text.regionMatches(true, start, - zoneName, 0, zoneName.length())) { - return j; - } - } - return -1; - } + private int matchZoneString(String text, int start, int zoneIndex) { + for (int j = 1; j <= 4; ++j) { + // Checking long and short zones [1 & 2], + // and long and short daylight [3 & 4]. + String[][] zoneStrings = formatData.getZoneStringsWrapper(); + String zoneName = zoneStrings[zoneIndex][j]; + if (text.regionMatches(true, start, zoneName, 0, zoneName.length())) { + return j; + } + } + return -1; + } - private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex) { - int index = standardIndex + 2; - String[][] zoneStrings = formatData.getZoneStringsWrapper(); - String zoneName = zoneStrings[zoneIndex][index]; - if (text.regionMatches(true, start, - zoneName, 0, zoneName.length())) { - return true; - } - return false; - } + private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex) { + int index = standardIndex + 2; + String[][] zoneStrings = formatData.getZoneStringsWrapper(); + String zoneName = zoneStrings[zoneIndex][index]; + if (text.regionMatches(true, start, zoneName, 0, zoneName.length())) { + return true; + } + return false; + } - /** - * find time zone 'text' matched zoneStrings and set to internal - * calendar. - */ - private int subParseZoneString(String text, int start) { - boolean useSameName = false; // true if standard and daylight time use the same abbreviation. - TimeZone currentTimeZone = getTimeZone(); - - // At this point, check for named time zones by looking through - // the locale data from the TimeZoneNames strings. - // Want to be able to parse both short and long forms. - int zoneIndex = - formatData.getZoneIndex (currentTimeZone.getID()); - TimeZone tz = null; - String[][] zoneStrings = formatData.getZoneStringsWrapper(); - int j = 0, i = 0; - if ((zoneIndex != -1) && ((j = matchZoneString(text, start, zoneIndex)) > 0)) { - if (j <= 2) { - useSameName = matchDSTString(text, start, zoneIndex, j); - } - tz = TimeZone.getTimeZone(zoneStrings[zoneIndex][0]); - i = zoneIndex; - } - if (tz == null) { - zoneIndex = - formatData.getZoneIndex (TimeZone.getDefault().getID()); - if ((zoneIndex != -1) && ((j = matchZoneString(text, start, zoneIndex)) > 0)) { - if (j <= 2) { - useSameName = matchDSTString(text, start, zoneIndex, j); - } - tz = TimeZone.getTimeZone(zoneStrings[zoneIndex][0]); - i = zoneIndex; - } - } - - if (tz == null) { - for (i = 0; i < zoneStrings.length; i++) { - if ((j = matchZoneString(text, start, i)) > 0) { - if (j <= 2) { - useSameName = matchDSTString(text, start, i, j); - } - tz = TimeZone.getTimeZone(zoneStrings[i][0]); - break; - } - } - } - if (tz != null) { // Matched any ? - if (!tz.equals(currentTimeZone)) { - setTimeZone(tz); - } - // If the time zone matched uses the same name - // (abbreviation) for both standard and daylight time, - // let the time zone in the Calendar decide which one. - if (!useSameName) { - calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset()); - calendar.set(Calendar.DST_OFFSET, - j >= 3 ? tz.getDSTSavings() : 0); - } - return (start + zoneStrings[i][j].length()); - } - return 0; - } + /** + * find time zone 'text' matched zoneStrings and set to internal calendar. + */ + private int subParseZoneString(String text, int start) { + boolean useSameName = false; // true if standard and daylight time use the same abbreviation. + TimeZone currentTimeZone = getTimeZone(); + + // At this point, check for named time zones by looking through + // the locale data from the TimeZoneNames strings. + // Want to be able to parse both short and long forms. + int zoneIndex = formatData.getZoneIndex(currentTimeZone.getID()); + TimeZone tz = null; + String[][] zoneStrings = formatData.getZoneStringsWrapper(); + int j = 0, i = 0; + if ((zoneIndex != -1) && ((j = matchZoneString(text, start, zoneIndex)) > 0)) { + if (j <= 2) { + useSameName = matchDSTString(text, start, zoneIndex, j); + } + tz = TimeZone.getTimeZone(zoneStrings[zoneIndex][0]); + i = zoneIndex; + } + if (tz == null) { + zoneIndex = formatData.getZoneIndex(TimeZone.getDefault().getID()); + if ((zoneIndex != -1) && ((j = matchZoneString(text, start, zoneIndex)) > 0)) { + if (j <= 2) { + useSameName = matchDSTString(text, start, zoneIndex, j); + } + tz = TimeZone.getTimeZone(zoneStrings[zoneIndex][0]); + i = zoneIndex; + } + } + + if (tz == null) { + for (i = 0; i < zoneStrings.length; i++) { + if ((j = matchZoneString(text, start, i)) > 0) { + if (j <= 2) { + useSameName = matchDSTString(text, start, i, j); + } + tz = TimeZone.getTimeZone(zoneStrings[i][0]); + break; + } + } + } + if (tz != null) { // Matched any ? + if (!tz.equals(currentTimeZone)) { + setTimeZone(tz); + } + // If the time zone matched uses the same name + // (abbreviation) for both standard and daylight time, + // let the time zone in the Calendar decide which one. + if (!useSameName) { + calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset()); + calendar.set(Calendar.DST_OFFSET, j >= 3 ? tz.getDSTSavings() : 0); + } + return (start + zoneStrings[i][j].length()); + } + return 0; + } - /** - * Private member function that converts the parsed date strings into - * timeFields. Returns -start (for ParsePosition) if failed. - * @param text the time text to be parsed. - * @param start where to start parsing. - * @param ch the pattern character for the date field text to be parsed. - * @param count the count of a pattern character. - * @param obeyCount if true, then the next field directly abuts this one, - * and we should use the count to know when to stop parsing. - * @param ambiguousYear return parameter; upon return, if ambiguousYear[0] - * is true, then a two-digit year was parsed and may need to be readjusted. - * @param origPos origPos.errorIndex is used to return an error index - * at which a parse error occurred, if matching failure occurs. - * @return the new start position if matching succeeded; -1 indicating - * matching failure, otherwise. In case matching failure occurred, - * an error index is set to origPos.errorIndex. - */ - private int subParse(String text, int start, int patternCharIndex, int count, - boolean obeyCount, boolean[] ambiguousYear, - ParsePosition origPos) - { - Number number = null; - int value = 0; - ParsePosition pos = new ParsePosition(0); - pos.index = start; - int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; - - // If there are any spaces here, skip over them. If we hit the end - // of the string, then fail. - for (;;) { - if (pos.index >= text.length()) { - origPos.errorIndex = start; - return -1; - } - char c = text.charAt(pos.index); - if (c != ' ' && c != '\t') break; - ++pos.index; - } - - // We handle a few special cases here where we need to parse - // a number value. We handle further, more generic cases below. We need - // to handle some of them here because some fields require extra processing on - // the parsed value. - if (patternCharIndex == 4 /*HOUR_OF_DAY1_FIELD*/ || - patternCharIndex == 15 /*HOUR1_FIELD*/ || - (patternCharIndex == 2 /*MONTH_FIELD*/ && count <= 2) || - patternCharIndex == 1) - { - // It would be good to unify this with the obeyCount logic below, - // but that's going to be difficult. - if (obeyCount) - { - if ((start+count) > text.length()) { - origPos.errorIndex = start; - return -1; - } - number = numberFormat.parse(text.substring(0, start+count), pos); - } - else number = numberFormat.parse(text, pos); - if (number == null) { - if (patternCharIndex != 1 || calendar instanceof GregorianCalendar) { - origPos.errorIndex = pos.index; - return -1; - } - } else { - value = number.intValue(); - } - } - - boolean useDateFormatSymbols = useDateFormatSymbols(); - - int index; - switch (patternCharIndex) - { - case 0: // 'G' - ERA - if (useDateFormatSymbols) { - if ((index = matchString(text, start, Calendar.ERA, formatData.getEras())) > 0) { - return index; - } - } else { - Map map = calendar.getDisplayNames(field, - Calendar.ALL_STYLES, - locale); - if ((index = matchString(text, start, field, map)) > 0) { - return index; - } - } - origPos.errorIndex = pos.index; - return -1; - - case 1: // 'y' - YEAR - if (!(calendar instanceof GregorianCalendar)) { - // calendar might have text representations for year values, - // such as "\u5143" in JapaneseImperialCalendar. - int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; - Map map = calendar.getDisplayNames(field, style, locale); - if (map != null) { - if ((index = matchString(text, start, field, map)) > 0) { - return index; - } - } - calendar.set(field, value); - return pos.index; - } - - // If there are 3 or more YEAR pattern characters, this indicates - // that the year value is to be treated literally, without any - // two-digit year adjustments (e.g., from "01" to 2001). Otherwise - // we made adjustments to place the 2-digit year in the proper - // century, for parsed strings from "00" to "99". Any other string - // is treated literally: "2250", "-1", "1", "002". - if (count <= 2 && (pos.index - start) == 2 - && Character.isDigit(text.charAt(start)) - && Character.isDigit(text.charAt(start+1))) - { - // Assume for example that the defaultCenturyStart is 6/18/1903. - // This means that two-digit years will be forced into the range - // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 - // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond - // to 1904, 1905, etc. If the year is 03, then it is 2003 if the - // other fields specify a date before 6/18, or 1903 if they specify a - // date afterwards. As a result, 03 is an ambiguous year. All other - // two-digit years are unambiguous. - int ambiguousTwoDigitYear = defaultCenturyStartYear % 100; - ambiguousYear[0] = value == ambiguousTwoDigitYear; - value += (defaultCenturyStartYear/100)*100 + - (value < ambiguousTwoDigitYear ? 100 : 0); - } - calendar.set(Calendar.YEAR, value); - return pos.index; - - case 2: // 'M' - MONTH - if (count <= 2) // i.e., M or MM. - { - // Don't want to parse the month if it is a string - // while pattern uses numeric style: M or MM. - // [We computed 'value' above.] - calendar.set(Calendar.MONTH, value - 1); - return pos.index; - } - else - { - if (useDateFormatSymbols) { - // count >= 3 // i.e., MMM or MMMM - // Want to be able to parse both short and long forms. - // Try count == 4 first: - int newStart = 0; - if ((newStart=matchString(text, start, Calendar.MONTH, - formatData.getMonths())) > 0) - return newStart; - else // count == 4 failed, now try count == 3 - if ((index = matchString(text, start, Calendar.MONTH, - formatData.getShortMonths())) > 0) { - return index; - } - } else { - Map map = calendar.getDisplayNames(field, - Calendar.ALL_STYLES, - locale); - if ((index = matchString(text, start, field, map)) > 0) { - return index; - } - } - } - origPos.errorIndex = pos.index; - return -1; - - case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 - // [We computed 'value' above.] - if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1) value = 0; - calendar.set(Calendar.HOUR_OF_DAY, value); - return pos.index; - - case 9: - { // 'E' - DAY_OF_WEEK - if (useDateFormatSymbols) { - // Want to be able to parse both short and long forms. - // Try count == 4 (DDDD) first: - int newStart = 0; - if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK, - formatData.getWeekdays())) > 0) - return newStart; - else // DDDD failed, now try DDD - if ((index = matchString(text, start, Calendar.DAY_OF_WEEK, - formatData.getShortWeekdays())) > 0) { - return index; - } - } else { - int[] styles = { Calendar.LONG, Calendar.SHORT }; - for (int style : styles) { - Map map = calendar.getDisplayNames(field, style, locale); - if ((index = matchString(text, start, field, map)) > 0) { - return index; - } - } - } - origPos.errorIndex = pos.index; - return -1; - } - - case 14: // 'a' - AM_PM - if (useDateFormatSymbols) { - if ((index = matchString(text, start, Calendar.AM_PM, formatData.getAmPmStrings())) > 0) { - return index; - } - } else { - Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); - if ((index = matchString(text, start, field, map)) > 0) { - return index; - } - } - origPos.errorIndex = pos.index; - return -1; - - case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM - // [We computed 'value' above.] - if (value == calendar.getLeastMaximum(Calendar.HOUR)+1) value = 0; - calendar.set(Calendar.HOUR, value); - return pos.index; - - case 17: // 'z' - ZONE_OFFSET - case 18: // 'Z' - ZONE_OFFSET - // First try to parse generic forms such as GMT-07:00. Do this first - // in case localized TimeZoneNames contains the string "GMT" - // for a zone; in that case, we don't want to match the first three - // characters of GMT+/-hh:mm etc. - { - int sign = 0; - int offset; - - // For time zones that have no known names, look for strings - // of the form: - // GMT[+-]hours:minutes or - // GMT. - if ((text.length() - start) >= GMT.length() && - text.regionMatches(true, start, GMT, 0, GMT.length())) { - int num; - calendar.set(Calendar.DST_OFFSET, 0); - pos.index = start + GMT.length(); - - try { // try-catch for "GMT" only time zone string - if( text.charAt(pos.index) == '+' ) { - sign = 1; - } else if( text.charAt(pos.index) == '-' ) { - sign = -1; - } - } - catch(StringIndexOutOfBoundsException e) {} - - if (sign == 0) { /* "GMT" without offset */ - calendar.set(Calendar.ZONE_OFFSET, 0 ); - return pos.index; - } - - // Look for hours. - try { - char c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - num = c - '0'; - } - if (text.charAt(++pos.index) != ':') { - c = text.charAt(pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - num *= 10; - num += c - '0'; - pos.index++; - } - } - if (num > 23) { - origPos.errorIndex = pos.index - 1; - return -1; // Wasn't actually a number. - } - if (text.charAt(pos.index) != ':') { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - } - catch(StringIndexOutOfBoundsException e) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - - // Look for minutes. - offset = num * 60; - try { - char c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - num = c - '0'; - c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - num *= 10; - num += c - '0'; - } - } - - if (num > 59) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - } - catch(StringIndexOutOfBoundsException e) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - offset += num; - - // Fall through for final processing below of 'offset' and 'sign'. - } - else { - // At this point, check for named time zones by looking through - // the locale data from the TimeZoneNames strings. - // Want to be able to parse both short and long forms. - int i = subParseZoneString(text, pos.index); - if (i != 0) { - return i; - } - - // As a last resort, look for numeric timezones of the form - // [+-]hhmm as specified by RFC 822. This code is actually - // a little more permissive than RFC 822. It will try to do - // its best with numbers that aren't strictly 4 digits long. - try { - if( text.charAt(pos.index) == '+' ) { - sign = 1; - } else if( text.charAt(pos.index) == '-' ) { - sign = -1; - } - if (sign == 0) { - origPos.errorIndex = pos.index; - return -1; - } - - // Look for hh. - int hours = 0; - char c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - hours = c - '0'; - c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - hours *= 10; - hours += c - '0'; - } - } - if (hours > 23) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - - // Look for mm. - int minutes = 0; - c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - minutes = c - '0'; - c = text.charAt(++pos.index); - if (c < '0' || c > '9') { /* must be from '0' to '9'. */ - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } else { - minutes *= 10; - minutes += c - '0'; - } - } - - if (minutes > 59) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - - offset = hours * 60 + minutes; - } catch(StringIndexOutOfBoundsException e) { - origPos.errorIndex = pos.index; - return -1; // Wasn't actually a number. - } - } - - // Do the final processing for both of the above cases. We only - // arrive here if the form GMT+/-... or an RFC 822 form was seen. - if (sign != 0) - { - offset *= millisPerMinute * sign; - calendar.set(Calendar.ZONE_OFFSET, offset); - calendar.set(Calendar.DST_OFFSET, 0); - return ++pos.index; - } - } - - // All efforts to parse a zone failed. - origPos.errorIndex = pos.index; - return -1; - - default: - // case 3: // 'd' - DATE - // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 - // case 6: // 'm' - MINUTE - // case 7: // 's' - SECOND - // case 8: // 'S' - MILLISECOND - // case 10: // 'D' - DAY_OF_YEAR - // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH - // case 12: // 'w' - WEEK_OF_YEAR - // case 13: // 'W' - WEEK_OF_MONTH - // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM - - // Handle "generic" fields - if (obeyCount) - { - if ((start+count) > text.length()) { - origPos.errorIndex = pos.index; - return -1; - } - number = numberFormat.parse(text.substring(0, start+count), pos); - } - else number = numberFormat.parse(text, pos); - if (number != null) { - calendar.set(field, number.intValue()); - return pos.index; - } - origPos.errorIndex = pos.index; - return -1; - } - } + /** + * Private member function that converts the parsed date strings into + * timeFields. Returns -start (for ParsePosition) if failed. + * + * @param text the time text to be parsed. + * @param start where to start parsing. + * @param ch the pattern character for the date field text to be + * parsed. + * @param count the count of a pattern character. + * @param obeyCount if true, then the next field directly abuts this one, + * and we should use the count to know when to stop + * parsing. + * @param ambiguousYear return parameter; upon return, if ambiguousYear[0] is + * true, then a two-digit year was parsed and may need to + * be readjusted. + * @param origPos origPos.errorIndex is used to return an error index at + * which a parse error occurred, if matching failure + * occurs. + * @return the new start position if matching succeeded; -1 indicating matching + * failure, otherwise. In case matching failure occurred, an error index + * is set to origPos.errorIndex. + */ + private int subParse(String text, int start, int patternCharIndex, int count, boolean obeyCount, + boolean[] ambiguousYear, ParsePosition origPos) { + Number number = null; + int value = 0; + ParsePosition pos = new ParsePosition(0); + pos.index = start; + int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; + + // If there are any spaces here, skip over them. If we hit the end + // of the string, then fail. + for (;;) { + if (pos.index >= text.length()) { + origPos.errorIndex = start; + return -1; + } + char c = text.charAt(pos.index); + if (c != ' ' && c != '\t') + break; + ++pos.index; + } + + // We handle a few special cases here where we need to parse + // a number value. We handle further, more generic cases below. We need + // to handle some of them here because some fields require extra processing on + // the parsed value. + if (patternCharIndex == 4 /* HOUR_OF_DAY1_FIELD */ || patternCharIndex == 15 /* HOUR1_FIELD */ + || (patternCharIndex == 2 /* MONTH_FIELD */ && count <= 2) || patternCharIndex == 1) { + // It would be good to unify this with the obeyCount logic below, + // but that's going to be difficult. + if (obeyCount) { + if ((start + count) > text.length()) { + origPos.errorIndex = start; + return -1; + } + number = numberFormat.parse(text.substring(0, start + count), pos); + } else + number = numberFormat.parse(text, pos); + if (number == null) { + if (patternCharIndex != 1 || calendar instanceof GregorianCalendar) { + origPos.errorIndex = pos.index; + return -1; + } + } else { + value = number.intValue(); + } + } + + boolean useDateFormatSymbols = useDateFormatSymbols(); + + int index; + switch (patternCharIndex) { + case 0: // 'G' - ERA + if (useDateFormatSymbols) { + if ((index = matchString(text, start, Calendar.ERA, formatData.getEras())) > 0) { + return index; + } + } else { + Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); + if ((index = matchString(text, start, field, map)) > 0) { + return index; + } + } + origPos.errorIndex = pos.index; + return -1; + + case 1: // 'y' - YEAR + if (!(calendar instanceof GregorianCalendar)) { + // calendar might have text representations for year values, + // such as "\u5143" in JapaneseImperialCalendar. + int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; + Map map = calendar.getDisplayNames(field, style, locale); + if (map != null) { + if ((index = matchString(text, start, field, map)) > 0) { + return index; + } + } + calendar.set(field, value); + return pos.index; + } + + // If there are 3 or more YEAR pattern characters, this indicates + // that the year value is to be treated literally, without any + // two-digit year adjustments (e.g., from "01" to 2001). Otherwise + // we made adjustments to place the 2-digit year in the proper + // century, for parsed strings from "00" to "99". Any other string + // is treated literally: "2250", "-1", "1", "002". + if (count <= 2 && (pos.index - start) == 2 && Character.isDigit(text.charAt(start)) + && Character.isDigit(text.charAt(start + 1))) { + // Assume for example that the defaultCenturyStart is 6/18/1903. + // This means that two-digit years will be forced into the range + // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 + // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond + // to 1904, 1905, etc. If the year is 03, then it is 2003 if the + // other fields specify a date before 6/18, or 1903 if they specify a + // date afterwards. As a result, 03 is an ambiguous year. All other + // two-digit years are unambiguous. + int ambiguousTwoDigitYear = defaultCenturyStartYear % 100; + ambiguousYear[0] = value == ambiguousTwoDigitYear; + value += (defaultCenturyStartYear / 100) * 100 + (value < ambiguousTwoDigitYear ? 100 : 0); + } + calendar.set(Calendar.YEAR, value); + return pos.index; + + case 2: // 'M' - MONTH + if (count <= 2) // i.e., M or MM. + { + // Don't want to parse the month if it is a string + // while pattern uses numeric style: M or MM. + // [We computed 'value' above.] + calendar.set(Calendar.MONTH, value - 1); + return pos.index; + } else { + if (useDateFormatSymbols) { + // count >= 3 // i.e., MMM or MMMM + // Want to be able to parse both short and long forms. + // Try count == 4 first: + int newStart = 0; + if ((newStart = matchString(text, start, Calendar.MONTH, formatData.getMonths())) > 0) + return newStart; + else // count == 4 failed, now try count == 3 + if ((index = matchString(text, start, Calendar.MONTH, formatData.getShortMonths())) > 0) { + return index; + } + } else { + Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); + if ((index = matchString(text, start, field, map)) > 0) { + return index; + } + } + } + origPos.errorIndex = pos.index; + return -1; + + case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 + // [We computed 'value' above.] + if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1) + value = 0; + calendar.set(Calendar.HOUR_OF_DAY, value); + return pos.index; + + case 9: { // 'E' - DAY_OF_WEEK + if (useDateFormatSymbols) { + // Want to be able to parse both short and long forms. + // Try count == 4 (DDDD) first: + int newStart = 0; + if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.getWeekdays())) > 0) + return newStart; + else // DDDD failed, now try DDD + if ((index = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.getShortWeekdays())) > 0) { + return index; + } + } else { + int[] styles = { Calendar.LONG, Calendar.SHORT }; + for (int style : styles) { + Map map = calendar.getDisplayNames(field, style, locale); + if ((index = matchString(text, start, field, map)) > 0) { + return index; + } + } + } + origPos.errorIndex = pos.index; + return -1; + } + + case 14: // 'a' - AM_PM + if (useDateFormatSymbols) { + if ((index = matchString(text, start, Calendar.AM_PM, formatData.getAmPmStrings())) > 0) { + return index; + } + } else { + Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); + if ((index = matchString(text, start, field, map)) > 0) { + return index; + } + } + origPos.errorIndex = pos.index; + return -1; + + case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM + // [We computed 'value' above.] + if (value == calendar.getLeastMaximum(Calendar.HOUR) + 1) + value = 0; + calendar.set(Calendar.HOUR, value); + return pos.index; + + case 17: // 'z' - ZONE_OFFSET + case 18: // 'Z' - ZONE_OFFSET + // First try to parse generic forms such as GMT-07:00. Do this first + // in case localized TimeZoneNames contains the string "GMT" + // for a zone; in that case, we don't want to match the first three + // characters of GMT+/-hh:mm etc. + { + int sign = 0; + int offset; + + // For time zones that have no known names, look for strings + // of the form: + // GMT[+-]hours:minutes or + // GMT. + if ((text.length() - start) >= GMT.length() && text.regionMatches(true, start, GMT, 0, GMT.length())) { + int num; + calendar.set(Calendar.DST_OFFSET, 0); + pos.index = start + GMT.length(); + + try { // try-catch for "GMT" only time zone string + if (text.charAt(pos.index) == '+') { + sign = 1; + } else if (text.charAt(pos.index) == '-') { + sign = -1; + } + } catch (StringIndexOutOfBoundsException e) { + } + + if (sign == 0) { /* "GMT" without offset */ + calendar.set(Calendar.ZONE_OFFSET, 0); + return pos.index; + } + + // Look for hours. + try { + char c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + num = c - '0'; + } + if (text.charAt(++pos.index) != ':') { + c = text.charAt(pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + num *= 10; + num += c - '0'; + pos.index++; + } + } + if (num > 23) { + origPos.errorIndex = pos.index - 1; + return -1; // Wasn't actually a number. + } + if (text.charAt(pos.index) != ':') { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + } catch (StringIndexOutOfBoundsException e) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + + // Look for minutes. + offset = num * 60; + try { + char c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + num = c - '0'; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + num *= 10; + num += c - '0'; + } + } + + if (num > 59) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + } catch (StringIndexOutOfBoundsException e) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + offset += num; + + // Fall through for final processing below of 'offset' and 'sign'. + } else { + // At this point, check for named time zones by looking through + // the locale data from the TimeZoneNames strings. + // Want to be able to parse both short and long forms. + int i = subParseZoneString(text, pos.index); + if (i != 0) { + return i; + } + + // As a last resort, look for numeric timezones of the form + // [+-]hhmm as specified by RFC 822. This code is actually + // a little more permissive than RFC 822. It will try to do + // its best with numbers that aren't strictly 4 digits long. + try { + if (text.charAt(pos.index) == '+') { + sign = 1; + } else if (text.charAt(pos.index) == '-') { + sign = -1; + } + if (sign == 0) { + origPos.errorIndex = pos.index; + return -1; + } + + // Look for hh. + int hours = 0; + char c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + hours = c - '0'; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + hours *= 10; + hours += c - '0'; + } + } + if (hours > 23) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + + // Look for mm. + int minutes = 0; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + minutes = c - '0'; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } else { + minutes *= 10; + minutes += c - '0'; + } + } + + if (minutes > 59) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + + offset = hours * 60 + minutes; + } catch (StringIndexOutOfBoundsException e) { + origPos.errorIndex = pos.index; + return -1; // Wasn't actually a number. + } + } + + // Do the final processing for both of the above cases. We only + // arrive here if the form GMT+/-... or an RFC 822 form was seen. + if (sign != 0) { + offset *= millisPerMinute * sign; + calendar.set(Calendar.ZONE_OFFSET, offset); + calendar.set(Calendar.DST_OFFSET, 0); + return ++pos.index; + } + } + + // All efforts to parse a zone failed. + origPos.errorIndex = pos.index; + return -1; + + default: + // case 3: // 'd' - DATE + // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 + // case 6: // 'm' - MINUTE + // case 7: // 's' - SECOND + // case 8: // 'S' - MILLISECOND + // case 10: // 'D' - DAY_OF_YEAR + // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH + // case 12: // 'w' - WEEK_OF_YEAR + // case 13: // 'W' - WEEK_OF_MONTH + // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM + + // Handle "generic" fields + if (obeyCount) { + if ((start + count) > text.length()) { + origPos.errorIndex = pos.index; + return -1; + } + number = numberFormat.parse(text.substring(0, start + count), pos); + } else + number = numberFormat.parse(text, pos); + if (number != null) { + calendar.set(field, number.intValue()); + return pos.index; + } + origPos.errorIndex = pos.index; + return -1; + } + } - private final String getCalendarName() { - return calendar.getClass().getName(); - } + private final String getCalendarName() { + return getCalendar().getClass().getName(); + } - private boolean useDateFormatSymbols() { - if (useDateFormatSymbols) { - return true; - } - return isGregorianCalendar() || locale == null; - } + private boolean useDateFormatSymbols() { + if (useDateFormatSymbols) { + return true; + } + return isGregorianCalendar() || locale == null; + } - private boolean isGregorianCalendar() { - return "java.util.GregorianCalendar".equals(getCalendarName()); - } + private boolean isGregorianCalendar() { + return "java.util.GregorianCalendar".equals(getCalendarName()); + } - /** - * Translates a pattern, mapping each character in the from string to the - * corresponding character in the to string. - * - * @exception IllegalArgumentException if the given pattern is invalid - */ - private String translatePattern(String pattern, String from, String to) { - StringBuilder result = new StringBuilder(); - boolean inQuote = false; - for (int i = 0; i < pattern.length(); ++i) { - char c = pattern.charAt(i); - if (inQuote) { - if (c == '\'') - inQuote = false; - } - else { - if (c == '\'') - inQuote = true; - else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { - int ci = from.indexOf(c); - if (ci == -1) - throw new IllegalArgumentException("Illegal pattern " + - " character '" + - c + "'"); - c = to.charAt(ci); - } - } - result.append(c); - } - if (inQuote) - throw new IllegalArgumentException("Unfinished quote in pattern"); - return result.toString(); - } + /** + * Translates a pattern, mapping each character in the from string to the + * corresponding character in the to string. + * + * @exception IllegalArgumentException if the given pattern is invalid + */ + private String translatePattern(String pattern, String from, String to) { + StringBuilder result = new StringBuilder(); + boolean inQuote = false; + for (int i = 0; i < pattern.length(); ++i) { + char c = pattern.charAt(i); + if (inQuote) { + if (c == '\'') + inQuote = false; + } else { + if (c == '\'') + inQuote = true; + else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + int ci = from.indexOf(c); + if (ci == -1) + throw new IllegalArgumentException("Illegal pattern " + " character '" + c + "'"); + c = to.charAt(ci); + } + } + result.append(c); + } + if (inQuote) + throw new IllegalArgumentException("Unfinished quote in pattern"); + return result.toString(); + } - /** - * Returns a pattern string describing this date format. - * - * @return a pattern string describing this date format. - */ - public String toPattern() { - return pattern; - } + /** + * Returns a pattern string describing this date format. + * + * @return a pattern string describing this date format. + */ + public String toPattern() { + return pattern; + } - /** - * Returns a localized pattern string describing this date format. - * - * @return a localized pattern string describing this date format. - */ - public String toLocalizedPattern() { - return translatePattern(pattern, - DateFormatSymbols.patternChars, - formatData.getLocalPatternChars()); - } + /** + * Returns a localized pattern string describing this date format. + * + * @return a localized pattern string describing this date format. + */ + public String toLocalizedPattern() { + return translatePattern(pattern, DateFormatSymbols.patternChars, formatData.getLocalPatternChars()); + } - /** - * Applies the given pattern string to this date format. - * - * @param pattern the new date and time pattern for this date format - * @exception NullPointerException if the given pattern is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - public void applyPattern (String pattern) - { - compiledPattern = compile(pattern); - this.pattern = pattern; - } + /** + * Applies the given pattern string to this date format. + * + * @param pattern the new date and time pattern for this date format + * @exception NullPointerException if the given pattern is null + * @exception IllegalArgumentException if the given pattern is invalid + */ + public void applyPattern(String pattern) { + compiledPattern = compile(pattern); + this.pattern = pattern; + } - /** - * Applies the given localized pattern string to this date format. - * - * @param pattern a String to be mapped to the new date and time format - * pattern for this format - * @exception NullPointerException if the given pattern is null - * @exception IllegalArgumentException if the given pattern is invalid - */ - public void applyLocalizedPattern(String pattern) { - String p = translatePattern(pattern, - formatData.getLocalPatternChars(), - DateFormatSymbols.patternChars); - compiledPattern = compile(p); - this.pattern = p; - } + /** + * Applies the given localized pattern string to this date format. + * + * @param pattern a String to be mapped to the new date and time format pattern + * for this format + * @exception NullPointerException if the given pattern is null + * @exception IllegalArgumentException if the given pattern is invalid + */ + public void applyLocalizedPattern(String pattern) { + String p = translatePattern(pattern, formatData.getLocalPatternChars(), DateFormatSymbols.patternChars); + compiledPattern = compile(p); + this.pattern = p; + } - /** - * Gets a copy of the date and time format symbols of this date format. - * - * @return the date and time format symbols of this date format - * @see #setDateFormatSymbols - */ - public DateFormatSymbols getDateFormatSymbols() - { - return (DateFormatSymbols)formatData.clone(); - } + /** + * Gets a copy of the date and time format symbols of this date format. + * + * @return the date and time format symbols of this date format + * @see #setDateFormatSymbols + */ + public DateFormatSymbols getDateFormatSymbols() { + return (DateFormatSymbols) formatData.clone(); + } - /** - * Sets the date and time format symbols of this date format. - * - * @param newFormatSymbols the new date and time format symbols - * @exception NullPointerException if the given newFormatSymbols is null - * @see #getDateFormatSymbols - */ - public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) - { - this.formatData = (DateFormatSymbols)newFormatSymbols.clone(); - useDateFormatSymbols = true; - } + /** + * Sets the date and time format symbols of this date format. + * + * @param newFormatSymbols the new date and time format symbols + * @exception NullPointerException if the given newFormatSymbols is null + * @see #getDateFormatSymbols + */ + public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) { + this.formatData = (DateFormatSymbols) newFormatSymbols.clone(); + useDateFormatSymbols = true; + } - /** - * Creates a copy of this SimpleDateFormat. This also - * clones the format's date format symbols. - * - * @return a clone of this SimpleDateFormat - */ - @Override - public Object clone() { - SimpleDateFormat other = (SimpleDateFormat) super.clone(); - other.formatData = (DateFormatSymbols) formatData.clone(); - return other; - } + /** + * Creates a copy of this SimpleDateFormat. This also clones the + * format's date format symbols. + * + * @return a clone of this SimpleDateFormat + */ + @Override + public Object clone() { + SimpleDateFormat other = (SimpleDateFormat) super.clone(); + other.formatData = (DateFormatSymbols) formatData.clone(); + return other; + } - /** - * Returns the hash code value for this SimpleDateFormat object. - * - * @return the hash code value for this SimpleDateFormat object. - */ - @Override - public int hashCode() - { - return pattern.hashCode(); - // just enough fields for a reasonable distribution - } + /** + * Returns the hash code value for this SimpleDateFormat object. + * + * @return the hash code value for this SimpleDateFormat object. + */ + @Override + public int hashCode() { + return pattern.hashCode(); + // just enough fields for a reasonable distribution + } - /** - * Compares the given object with this SimpleDateFormat for - * equality. - * - * @return true if the given object is equal to this - * SimpleDateFormat - */ - @Override - public boolean equals(Object obj) - { - if (!super.equals(obj)) return false; // super does class check - SimpleDateFormat that = (SimpleDateFormat) obj; - return (pattern.equals(that.pattern) - && formatData.equals(that.formatData)); - } + /** + * Compares the given object with this SimpleDateFormat for + * equality. + * + * @return true if the given object is equal to this + * SimpleDateFormat + */ + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) + return false; // super does class check + SimpleDateFormat that = (SimpleDateFormat) obj; + return (pattern.equals(that.pattern) && formatData.equals(that.formatData)); + } // /** // * After reading an object from the input stream, the format @@ -2195,4 +2251,5 @@ public boolean equals(Object obj) // } // } // } + } diff --git a/sources/net.sf.j2s.java.core/src/javajs/util/M4d.java b/sources/net.sf.j2s.java.core/src/javajs/util/M4d.java index a234e9ce2..076fa1651 100644 --- a/sources/net.sf.j2s.java.core/src/javajs/util/M4d.java +++ b/sources/net.sf.j2s.java.core/src/javajs/util/M4d.java @@ -336,7 +336,8 @@ public void setTranslation(T3 trans) { * @param v * the new value */ - public void setElement(int row, int col, double v) { + @Override +public void setElement(int row, int col, double v) { if (row < 3 && col < 3) { set33(row, col, v); return; @@ -379,7 +380,8 @@ public void setElement(int row, int col, double v) { * the column number to be retrieved (zero indexed) * @return the value at the indexed element */ - public double getElement(int row, int col) { + @Override +public double getElement(int row, int col) { if (row < 3 && col < 3) return get33(row, col); if (row > 3 || col > 3) { diff --git a/sources/net.sf.j2s.java.core/src/javajs/util/VideoReader.java b/sources/net.sf.j2s.java.core/src/javajs/util/VideoReader.java index 98da70f4c..9942d73a1 100644 --- a/sources/net.sf.j2s.java.core/src/javajs/util/VideoReader.java +++ b/sources/net.sf.j2s.java.core/src/javajs/util/VideoReader.java @@ -84,6 +84,11 @@ protected void readBlock(List> contents) throws IOException int pt = this.pt; blockLen = readInt(); blockType = readString(4); + if (blockLen == 1) { + // special flag for long length not int + // presuming here it is not THAT large + blockLen = (int) is.readLong(); + } if (verbose) System.out.println(blockType + "\t" + pt + "\t0x" + Long.toHexString(pt) + "\t" + blockLen); Map map = new Hashtable<>(); diff --git a/sources/net.sf.j2s.java.core/src/sun/font/FontDesignMetrics.java b/sources/net.sf.j2s.java.core/src/sun/font/FontDesignMetrics.java index d4a8a864d..984862360 100644 --- a/sources/net.sf.j2s.java.core/src/sun/font/FontDesignMetrics.java +++ b/sources/net.sf.j2s.java.core/src/sun/font/FontDesignMetrics.java @@ -509,7 +509,6 @@ public int getDescent() { private void _getMetrics() { if (ascent >= 0) return; -// 秘fm = new JSFontMetrics(font); ascent = JSFontMetrics.fontAscent(font); descent = JSFontMetrics.fontDescent(font); leading = JSFontMetrics.fontLeading(font); @@ -568,7 +567,7 @@ public int getHeight() { public Rectangle2D 秘getStringBounds(String s) { _getMetrics(); - return new Rectangle2D.Float(0, -ascent, stringWidth(s), ascent + descent + leading); + return new Rectangle2D.Float(0, -ascent, getWidth(s), ascent + descent + leading); } /** diff --git a/sources/net.sf.j2s.java.core/src/sun/font/StandardGlyphVector.java b/sources/net.sf.j2s.java.core/src/sun/font/StandardGlyphVector.java index d9d96ac44..e965df146 100644 --- a/sources/net.sf.j2s.java.core/src/sun/font/StandardGlyphVector.java +++ b/sources/net.sf.j2s.java.core/src/sun/font/StandardGlyphVector.java @@ -41,8 +41,10 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Float; import java.text.CharacterIterator; +import javajs.util.SB; import sun.awt.SunHints; import sun.font.GlyphLayout.GVData; import sun.java2d.loops.FontInfo; @@ -135,6 +137,54 @@ * to zero, used in superscript). */ public class StandardGlyphVector extends GlyphVector { + + private static class JSTextMetrics { + float width; + float actualBoundingBoxLeft; + float actualBoundingBoxAscent; + float actualBoundingBoxRight; + float actualBoundingBoxDescent; + float fontBoundingBoxAscent; + float fontBoundingBoxDescent; + float offsetX, offsetY; + private String text; + private float logicalWidth; + + + public JSTextMetrics(String text, JSTextMetrics tm) { + this.text = text; + this.width = tm.width; + this.actualBoundingBoxAscent = tm.actualBoundingBoxAscent; + this.actualBoundingBoxDescent = tm.actualBoundingBoxDescent; + this.actualBoundingBoxLeft = tm.actualBoundingBoxLeft; + this.actualBoundingBoxRight = tm.actualBoundingBoxRight; + this.fontBoundingBoxAscent = tm.fontBoundingBoxAscent; + this.fontBoundingBoxDescent = tm.fontBoundingBoxDescent; + this.logicalWidth = actualBoundingBoxRight - actualBoundingBoxLeft; + this.offsetX = -actualBoundingBoxLeft; + this.offsetY = actualBoundingBoxAscent + fontBoundingBoxDescent; +// System. intln("jstxtm" +// + " " + this.actualBoundingBoxLeft +// + " " + this.actualBoundingBoxRight +// + " " + width + " " + this.actualBoundingBoxAscent + " " + this.actualBoundingBoxDescent); + } + + protected Rectangle2D.Float getActualBounds(float x0, float y0) { + float x = -actualBoundingBoxLeft; + float y = -actualBoundingBoxAscent; + float w = actualBoundingBoxRight - x; + float h = actualBoundingBoxDescent - y; + return new Rectangle2D.Float(x + x0, y + y0, w, h); + } + + protected float getLogicalWidth() { + return logicalWidth; + } + + } + + + private JSTextMetrics[] jsMetrics; private Font font; private FontRenderContext frc; private int[] glyphs; // always @@ -142,7 +192,8 @@ public class StandardGlyphVector extends GlyphVector { private float[] positions; // only if not default advances private int[] charIndices; // only if interesting private int flags; // indicates whether positions, charIndices is interesting - + final private String text; // allows for unicode //BH SwingJS + private static final int UNINITIALIZED_FLAGS = -1; // transforms information @@ -153,7 +204,7 @@ public class StandardGlyphVector extends GlyphVector { private AffineTransform dtx; // device transform used for strike calculations, no translation private AffineTransform invdtx; // inverse of dtx or null if dtx is identity private AffineTransform frctx; // font render context transform, wish we could just share it - private Font2D fontxx2D; // basic strike-independent stuff + private Font2D font2D; // basic strike-independent stuff private GlyphStrike fsref; // font strike reference for glyphs with no per-glyph transform ///////////////////////////// @@ -161,15 +212,18 @@ public class StandardGlyphVector extends GlyphVector { ///////////////////////////// public StandardGlyphVector(Font font, String str, FontRenderContext frc) { + text = str; init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) { + this.text = new String(text); init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, int start, int count, FontRenderContext frc) { + this.text = new String(text); init(font, text, start, count, frc, UNINITIALIZED_FLAGS); } @@ -184,6 +238,7 @@ private float getTracking(Font font) { // used by GlyphLayout to construct a glyphvector public StandardGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { + text = new String(glyphs, 0, glyphs.length); initGlyphVector(font, frc, glyphs, positions, indices, flags); // this code should go into layout @@ -219,7 +274,7 @@ public StandardGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float } } - public void initGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, + public void initGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { this.font = font; this.frc = frc; @@ -240,12 +295,14 @@ public StandardGlyphVector(Font font, CharacterIterator iter, FontRenderContext c = iter.next()) { text[iter.getIndex() - offset] = c; } + this.text = new String(text); init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, int[] glyphs, FontRenderContext frc) { // !!! find callers of this // should be able to fully init from raw data, e.g. charmap, flags too. + this.text = new String(glyphs, 0, glyphs.length); this.font = font; this.frc = frc; this.flags = UNINITIALIZED_FLAGS; @@ -301,6 +358,8 @@ public FontRenderContext getFontRenderContext() { @Override public void performDefaultLayout() { + if (true) + return; positions = null; if (getTracking(font) == 0) { clearFlags(FLAG_HAS_POSITION_ADJUSTMENTS); @@ -387,25 +446,29 @@ public int[] getGlyphCharIndices(int start, int count, int[] result) { // !!! revisit for text-on-a-path, vertical @Override public Rectangle2D getLogicalBounds() { - setFRCTX(); - initPositions(); - - LineMetrics lm = font.getLineMetrics("", frc); - - float minX, minY, maxX, maxY; - // horiz only for now... - minX = 0; - minY = -lm.getAscent(); - maxX = 0; - maxY = lm.getDescent() + lm.getLeading(); - if (glyphs.length > 0) { - maxX = positions[positions.length - 2]; - } - - return new Rectangle2D.Float(minX, minY, maxX - minX, maxY - minY); + // SwingJS needs no more than this + return ((FontDesignMetrics) font.getFontMetrics()).秘getStringBounds(text); + +// +// setFRCTX(); +// initPositions(); +// +// LineMetrics lm = font.getLineMetrics("", frc); +// +// float minX, minY, maxX, maxY; +// // horiz only for now... +// minX = 0; +// minY = -lm.getAscent(); +// maxX = 0; +// maxY = lm.getDescent() + lm.getLeading(); +// if (glyphs.length > 0) { +// maxX = positions[positions.length - 2]; +// } +// +// return new Rectangle2D.Float(minX, minY, maxX - minX, maxY - minY); } - // !!! not cached, assume TextLayout will cache if necessary + // !!! not cached, assume TextLayout will cache if necessary @Override public Rectangle2D getVisualBounds() { return (Rectangle2D) getOutline(); @@ -549,6 +612,17 @@ public Shape getGlyphLogicalBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } + + + +/** + * @j2sNative debugger + */ + + + + + Shape[] lbcache = lbcacheRef; if (lbcacheRef == null) @@ -599,6 +673,8 @@ public Shape getGlyphVisualBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } + // SwingJS just same as GlyphOutline + return getGlyphOutline(ix).getBounds2D(); //original // Shape[] vbcache; // if (vbcacheRef == null || (vbcache = (Shape[])vbcacheRef.get()) == null) { @@ -606,19 +682,19 @@ public Shape getGlyphVisualBounds(int ix) { // vbcacheRef = new SoftReference(vbcache); // } //BH 2020.04.14 - - Shape[] vbcache = vbcacheRef; - if (vbcache == null) - vbcache = vbcacheRef = new Shape[glyphs.length] - ; - - Shape result = vbcache[ix]; - if (result == null) { - result = new DelegatingShape(getGlyphOutlineBounds(ix)); - vbcache[ix] = result; - } - - return result; +// +// Shape[] vbcache = vbcacheRef; +// if (vbcache == null) +// vbcache = vbcacheRef = new Shape[glyphs.length] +// ; +// +// Shape result = vbcache[ix]; +// if (result == null) { +// result = new DelegatingShape(getGlyphOutlineBounds(ix)); +// vbcache[ix] = result; +// } +// +// return result; } private Shape[] vbcacheRef; @@ -894,17 +970,17 @@ public float[] getGlyphInfo() { result[n] = x; result[n+1] = 0; - int glyphID = glyphs[i]; +// int glyphID = glyphs[i]; // GlyphStrike s = getGlyphStrike(i); // Point2D.Float adv = s.strike.getGlyphMetrics(glyphID); result[n+2] = w;//adv.x; result[n+3] = 0;//adv.y; -// Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D(); -// result[n+4] = (float)(vb.getMinX()); -// result[n+5] = (float)(vb.getMinY()); -// result[n+6] = (float)(vb.getWidth()); -// result[n+7] = (float)(vb.getHeight()); + Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D(); + result[n+4] = (float)(vb.getMinX()); + result[n+5] = (float)(vb.getMinY()); + result[n+6] = (float)(vb.getWidth()); + result[n+7] = (float)(vb.getHeight()); } return result; } @@ -1032,16 +1108,16 @@ private void setRenderTransform(double[] devTX) { // called by getGlyphsPixelBounds private final void setDTX(AffineTransform tx) { - if (!equalNonTranslateTX(dtx, tx)) { - resetDTX(getNonTranslateTX(tx)); - } +// if (!equalNonTranslateTX(dtx, tx)) { +// resetDTX(getNonTranslateTX(tx)); +// } } // called by most functions private final void setFRCTX() { - if (!equalNonTranslateTX(frctx, dtx)) { - resetDTX(getNonTranslateTX(frctx)); - } +// if (!equalNonTranslateTX(frctx, dtx)) { +// resetDTX(getNonTranslateTX(frctx)); +// } } /** @@ -1071,6 +1147,7 @@ private final void resetDTX(AffineTransform at) { * about "userGlyphs". */ private StandardGlyphVector(GlyphVector gv, FontRenderContext frc) { + this.text = ((StandardGlyphVector) gv).text; this.font = gv.getFont(); this.frc = frc; initFontData(); @@ -1114,17 +1191,18 @@ private StandardGlyphVector(GlyphVector gv, FontRenderContext frc) { * those as the missing glyph. */ int[] getValidatedGlyphs(int[] oglyphs) { - int len = oglyphs.length; - int[] vglyphs = new int[len]; - for (int i=0; i text.length) { throw new ArrayIndexOutOfBoundsException("start or count out of bounds"); } - + this.font = font; this.frc = frc; this.flags = flags; @@ -1162,7 +1240,8 @@ private void init(Font font, char[] text, int start, int count, } private void initFontData() { - //font2D = FontUtilities.getFont2D(font); + //SwingJS font2D = FontUtilities.getFont2D(font); + getJavaScriptMetrics(); float s = font.getSize2D(); if (font.isTransformed()) { ftx = font.getTransform(); @@ -1179,7 +1258,21 @@ private void initFontData() { resetDTX(getNonTranslateTX(frctx)); } - /** + private void getJavaScriptMetrics() { + // 1-based, with 0 being the full text + int n = text.length(); + jsMetrics = new JSTextMetrics[n + 1]; + if (n == 1) { + jsMetrics[0] = jsMetrics[1] = new JSTextMetrics(text, (JSTextMetrics) JSToolkit.getTextMetrics(null, font, text)); + return; + } + for (int i = n + 1; --i >= 0;) { + String t = i == 0 ? text : text.substring(i - 1, i); + jsMetrics[i] = new JSTextMetrics(t, (JSTextMetrics) JSToolkit.getTextMetrics(null, font, t)); + } + } + + /** * Copy glyph position data into a result array starting at the indicated * offset in the array. If the passed-in result array is null, a new * array will be allocated and returned. @@ -1217,12 +1310,21 @@ private Rectangle2D getGlyphOutlineBounds(int ix) { * Used by getOutline, getGlyphsOutline */ private Shape getGlyphsOutline(int start, int count, float x, float y) { - setFRCTX(); initPositions(); + // SwingJS just getting bounds here + if (start == 0 && count == glyphs.length) + return jsMetrics[0].getActualBounds(x, y); + //float h = JSFontMetrics.fontAscent(font); + Rectangle2D.Float r = jsMetrics[start + 1].getActualBounds(0, 0); + for (int i = 1, p = start + 1; i < count; i++, p++) { + Rectangle2D.Float r1 = jsMetrics[p].getActualBounds(positions[p * 2], positions[p * 2 + 1]); + r.add(r1); + } + r.x += x; + r.y += y; + return r; - float h = JSFontMetrics.fontAscent(font); - return new Rectangle2D.Float(x + positions[start * 2], y + positions[start * 2 + 1] - h, positions[count * 2], positions[count * 2 + 1] + h); // GeneralPath result = new GeneralPath(GeneralPath.WIND_NON_ZERO); // for (int i = start, e = start + count, n = start * 2; i < e; ++i, n += 2) { // float px = x + positions[n]; @@ -1357,7 +1459,9 @@ private void initPositions() { } } for (int i = 0, n = 2; i < glyphs.length; ++i, n += 2) { - getGlyphStrike(i).addDefaultGlyphAdvance(glyphs[i], pt); + pt.x += jsMetrics[i + 1].getLogicalWidth(); + // ignore y here - assumes horizontal left to right; + //getGlyphStrike(i).addDefaultGlyphAdvance(glyphs[i], pt); if (trackPt != null) { pt.x += trackPt.x; pt.y += trackPt.y; @@ -2016,4 +2120,72 @@ protected StringBuffer toStringBuffer(StringBuffer result) { return result; } } + +// tests + +// Java: +// +// java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=20]a=20.107422 d=4.3945312 h=25.15625 +// +// H outB java.awt.geom.Rectangle2D$Float[x=1.609375,y=-14.3125,w=11.21875,h=14.3125] +// H logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.107422,w=14.443359,h=25.15625] +// +// JavaScript: +// +// java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=20]a=20.10742 d=4.394 h=25.155420000000003 +// +// H outB java.awt.geom.Rectangle2D$Float[x=1.607568359375,y=-14.3700927734375,w=11.272583770751954,h=14.3700927734375] +// H logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.10742,w=14.487720489501953,h=25.155420000000003] +// +// jstxtm -1.607568359375 12.880152130126953 14.5 14.3700927734375 0.0 +// +// Java: +// +// j outB java.awt.geom.Rectangle2D$Float[x=-0.921875,y=-14.3125,w=3.984375,h=18.515625] +// j logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.107422,w=4.4433594,h=25.15625] +// +// JavaScript: +// +// j outB java.awt.geom.Rectangle2D$Float[x=-0.9214111328125001,y=-14.3700927734375,w=3.99931640625,h=18.59486083984375] +// j logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.10742,w=2.156494140625,h=25.155420000000003] +// +// jstxtm 0.9214111328125001 3.0779052734375 4.449999809265137 14.3700927734375 4.22476806640625 +// +// Java: +// +// Hj outB java.awt.geom.Rectangle2D$Float[x=1.609375,y=-14.3125,w=15.896484,h=18.515625] +// Hj logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.107422,w=18.886719,h=25.15625] +// Hj pos [0.0, 0.0, 14.443359, 0.0] +// +// JavaScript: +// +// Hj outB java.awt.geom.Rectangle2D$Float[x=1.607568359375,y=-14.3700927734375,w=15.970336914062502,h=18.59486083984375] +// Hj logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.10742,w=19.185473632812503,h=25.155420000000003] +// Hj pos [0, 0, 14.487720489501953, 0] +// +// jstxtm 0.9214111328125001 3.0779052734375 4.449999809265137 14.3700927734375 4.22476806640625 +// jstxtm -1.607568359375 12.880152130126953 14.5 14.3700927734375 0.0 +// jstxtm -1.607568359375 17.5779052734375 18.950000762939453 14.3700927734375 4.22476806640625 +// +// Java: +// +// HjHjHj outB java.awt.geom.Rectangle2D$Float[x=1.609375,y=-14.3125,w=53.66992,h=18.515625] +// HjHjHj logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.107422,w=56.660156,h=25.15625] +// HjHjHj pos [0.0, 0.0, 14.443359, 0.0, 18.886719, 0.0, 33.33008, 0.0, 37.773438, 0.0, 52.216797, 0.0] +// +// JavaScipt: +// +// HjHjHj outB java.awt.geom.Rectangle2D$Float[x=1.607568359375,y=-14.3700927734375,w=53.870336914062506,h=18.59486083984375] +// HjHjHj logB java.awt.geom.Rectangle2D$Float[x=0.0,y=-20.10742,w=57.08547363281251,h=25.155420000000003] +// HjHjHj pos [0, 0, 14.487720489501953, 0, 16.644214630126953, 0, 31.131935119628906, 0, 33.288429260253906, 0, 47.77614974975586, 0] +// +// jstxtm 0.9214111328125001 3.0779052734375 4.449999809265137 14.3700927734375 4.22476806640625 +// jstxtm -1.607568359375 12.880152130126953 14.5 14.3700927734375 0.0 +// jstxtm 0.9214111328125001 3.0779052734375 4.449999809265137 14.3700927734375 4.22476806640625 +// jstxtm -1.607568359375 12.880152130126953 14.5 14.3700927734375 0.0 +// jstxtm 0.9214111328125001 3.0779052734375 4.449999809265137 14.3700927734375 4.22476806640625 +// jstxtm -1.607568359375 12.880152130126953 14.5 14.3700927734375 0.0 +// jstxtm -1.607568359375 55.47790527343751 56.849998474121094 14.3700927734375 4.22476806640625 + } + diff --git a/sources/net.sf.j2s.java.core/src/swingjs/JSToolkit.java b/sources/net.sf.j2s.java.core/src/swingjs/JSToolkit.java index 71e02eea9..55337c5fc 100644 --- a/sources/net.sf.j2s.java.core/src/swingjs/JSToolkit.java +++ b/sources/net.sf.j2s.java.core/src/swingjs/JSToolkit.java @@ -249,7 +249,8 @@ public static float getStringWidth(HTML5CanvasContext2D context, Font font, /** * @j2sNative * context.font = fontInfo; - * w = Math.ceil(context.measureText(text).width); + * var tm = context.measureText(text); + * w = tm.actualBoundingBoxRight - tm.actualBoundingBoxLeft; */ { } @@ -260,8 +261,11 @@ public static Object getTextMetrics(HTML5CanvasContext2D context, Font font, String text) { if (text == null || text.length() == 0) return 0; + // Chrome delivers integer values, so we x 10 and then reduce. + // just setting this true generally; we don't need huge precision, I think. +// boolean isChrome = true || (/** @j2sNative J2S._isChrome || **/ false); @SuppressWarnings("unused") - String fontInfo = getCanvasFont(font); + String fontInfo = getCanvasFontScaled(font, 100);//isChrome ? 100 : 1); if (context == null) context = getDefaultCanvasContext2d(); Object tm = null; @@ -269,6 +273,14 @@ public static Object getTextMetrics(HTML5CanvasContext2D context, Font font, * @j2sNative * context.font = fontInfo; * tm = context.measureText(text); + tm = { "width":tm.width/100, + "actualBoundingBoxAscent":tm.actualBoundingBoxAscent/100, + "actualBoundingBoxDescent":tm.actualBoundingBoxDescent/100, + "actualBoundingBoxLeft":tm.actualBoundingBoxLeft/100, + "actualBoundingBoxRight":tm.actualBoundingBoxRight/100, + "fontBoundingBoxAscent":tm.fontBoundingBoxAscent/100, + "fontBoundingBoxDescent":tm.fontBoundingBoxDescent/100 + } */ { } @@ -885,6 +897,9 @@ public FontMetrics getFontMetrics(Font font) { * @return "italic bold 10pt Arial" */ public static String getCanvasFont(Font font) { + return getCanvasFontScaled(font, 1); + } + public static String getCanvasFontScaled(Font font, double scale) { String strStyle = ""; if (font.isItalic()) strStyle += "italic "; @@ -894,7 +909,7 @@ public static String getCanvasFont(Font font) { // for whatever reason, Java font points are much larger than HTML5 canvas // points font.getSize(); - return strStyle + font.getSize2D() + "px " + family; + return strStyle + (font.getSize2D() * scale) + "px " + family; } public static String getCSSFontFamilyName(String family) { diff --git a/sources/net.sf.j2s.java.core/src/swingjs/plaf/JSFrameUI.java b/sources/net.sf.j2s.java.core/src/swingjs/plaf/JSFrameUI.java index 92bbec20d..4fee73d80 100644 --- a/sources/net.sf.j2s.java.core/src/swingjs/plaf/JSFrameUI.java +++ b/sources/net.sf.j2s.java.core/src/swingjs/plaf/JSFrameUI.java @@ -14,6 +14,7 @@ import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; import javax.swing.Timer; import swingjs.JSUtil; @@ -180,9 +181,15 @@ public Object getEmbedded(String type) { if (dim.width > 0) { frame.setUndecorated(true); frame.setLocation(0, 0); + DOMNode.setStyles(containerNode, "width", dim.width + "px", "height", dim.height + "px"); String resize = DOMNode.getStyle(node, "resize"); - if (resize == "none") - frame.秘freezeBounds(dim.width, dim.height); + if (resize == "none") { + SwingUtilities.invokeLater(()->{ + // this allows the freeze to be after the + // currently executing reshape execution + frame.秘freezeBounds(dim.width, dim.height); + }); + } } else { DOMNode.setStyles(node, "position", "relative", "overflow", "hidden"); } diff --git a/sources/net.sf.j2s.java.core/src/test/Test_Date.java b/sources/net.sf.j2s.java.core/src/test/Test_Date.java new file mode 100644 index 000000000..82b42ed4f --- /dev/null +++ b/sources/net.sf.j2s.java.core/src/test/Test_Date.java @@ -0,0 +1,67 @@ +package test; + +import java.util.Date; +import java.text.SimpleDateFormat; + +strictfp class Test_Date extends Test_{ + + public static void main(String[] args) { + +// 1741542116297 +// 03/9/25 +// 03-09-2025 17:41:56 +// 0309251741 +// +// 1741542116297 +// 03/9/25 +// 03-09-2025 12:41:56 +// 0309251241 + long t = 1741542116297L; + Date d = new Date( "07/10/96 12:00 AM GMT"); + System.out.println(d); + String s; + + s = new SimpleDateFormat("M/d/yy h:mm").format(d.getTime()).toString(); + System.out.println(s); + + System.out.println(d.getTime()); + + s = new SimpleDateFormat("MM-dd-yy-hh-mm").format(d).toString(); + System.out.println(s); + + s = new SimpleDateFormat("MM-dd-yy-HH-mm").format(d).toString(); + System.out.println(s); + + s = new SimpleDateFormat("MM-dd-yy-kk-mm").format(d).toString(); + System.out.println(s); + + d = new Date( "07/10/96 7:00 AM"); + System.out.println(d); + + System.out.println(d.getTime()); + + s = new SimpleDateFormat("M-d-yyyy-hh-mm").format(d).toString(); + System.out.println(s); + + s = new SimpleDateFormat("M-d-yy-HH-mm").format(d).toString(); + System.out.println(s); + + s = new SimpleDateFormat("MM-dd-yy-kk-mm").format(d).toString(); + System.out.println(s); + + + d = new Date( "07/10/96 7:18 AM"); + System.out.println(d); + + + s = new SimpleDateFormat("M/d/yy h:mm").format(d.getTime()).toString(); + System.out.println(s); + s = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss").format(d).toString(); + System.out.println(s); + s = new SimpleDateFormat("MMddyyhhmm").format(d).toString(); + System.out.println(s); + + } + + +} \ No newline at end of file diff --git a/sources/net.sf.j2s.java.core/src/test/Test_Font.java b/sources/net.sf.j2s.java.core/src/test/Test_Font.java index 69acf5ba2..6f0e2a053 100644 --- a/sources/net.sf.j2s.java.core/src/test/Test_Font.java +++ b/sources/net.sf.j2s.java.core/src/test/Test_Font.java @@ -5,6 +5,8 @@ import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.text.NumberFormat; +import java.util.Locale; import javax.swing.JFrame; import javax.swing.JLabel; @@ -21,8 +23,33 @@ * */ public class Test_Font {//extends Test_ { + protected static String formatMDLFloat(float fl) { + String s, fs = ""; + int l; + NumberFormat nf = NumberFormat.getNumberInstance(Locale.ENGLISH); + nf.setMinimumIntegerDigits(1); + nf.setMaximumIntegerDigits(4); + nf.setMinimumFractionDigits(4); + nf.setMaximumFractionDigits(4); + nf.setGroupingUsed(false); + if (Double.isNaN(fl) || Double.isInfinite(fl)) + s = "0.0000"; + else + s = nf.format(fl); + l = 10 - s.length(); + for (int f = 0; f < l; f++) + fs += " "; + fs += s; + return fs; + } + public static void main(String[] args) { + + System.out.println(formatMDLFloat(2.3f)); + + if (true) + return; JLabel c = new JLabel("testing"); Graphics2D g = (Graphics2D) c.getGraphics(); assert (g == null); diff --git a/sources/net.sf.j2s.java.core/src/test/Test_J8_Stream.java b/sources/net.sf.j2s.java.core/src/test/Test_J8_Stream.java index 9257607fa..b9f6e0234 100644 --- a/sources/net.sf.j2s.java.core/src/test/Test_J8_Stream.java +++ b/sources/net.sf.j2s.java.core/src/test/Test_J8_Stream.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Spliterator; import java.util.Spliterator.OfInt; import java.util.function.BiConsumer; @@ -33,10 +34,20 @@ public static E returnSame(E elem) { } public class Test_J8_Stream extends Test_J8_Stream0 { - + @SuppressWarnings("unused") public static void main(String[] args) { + List list = new ArrayList<>(); + list.add(Integer.valueOf(33333)); + Set begSymCls = list + .stream() + .filter(b -> b == 33333) + .map(b -> 22322) + .collect(Collectors.toSet()); + + + String st = "test\ning\r\nnow"; String[] lines = st.split("\n"); int[] ptr = new int[1]; diff --git a/sources/net.sf.j2s.java.core/srcjs/js/j2sApplet.js b/sources/net.sf.j2s.java.core/srcjs/js/j2sApplet.js index 274894ac7..bf3283521 100644 --- a/sources/net.sf.j2s.java.core/srcjs/js/j2sApplet.js +++ b/sources/net.sf.j2s.java.core/srcjs/js/j2sApplet.js @@ -1,5 +1,8 @@ // j2sApplet.js BH = Bob Hanson hansonr@stolaf.edu +// BH 2025.04.20 adds Info.coreAssets:"coreAssets.zip" +// BH 2025.04.18 enables Info.readyFunction for headless apps +// BH 2025.04.17 adds option for explicit directory for core files different from j2sPath/core // BH 2024.11.09 makes equivalent J2S._debugCore and J2S._nozcore, as well as J2S._debugCode and J2S._nocore // BH 2024.10.03 adds two-finger tap as "click"; reinstates touch gestures lost when we went to pointerup 2023.11.01 // BH 2023.12.14 fixes resizing into application (making it smaller) @@ -2587,26 +2590,39 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { type = null; } + var thisPath, isFullName; if (type) { - type = type.toLowerCase().split(".")[0]; // package name only + var key = type.toLowerCase().substring(type.lastIndexOf("/") + 1); + key = key.toLowerCase().split(".")[0]; // package name only // return if type is already part of the set. - if (__coreSet.join("").indexOf(type) >= 0) + if (__coreSet.join("").indexOf(key) >= 0) return; + __coreSet.push(key); + __coreSet.sort(); + // create a concatenated lower-case name for a core file that // includes // all Java applets on the page + // only if this is not a full name - __coreSet.push(type); - __coreSet.sort(); - J2S._coreFiles = [ path + "/core/core" + __coreSet.join("") - + ".z.js" ]; + // 2025.04.17 adds option to give full (local) path to core file + isFullName = (type.indexOf("/") >= 0); + if (isFullName){ + // bypass core/package.js + J2S.Globals["core.registered"] = true; + } + thisPath = (isFullName ? type : path + "/core/core" + __coreSet.join("") + ".z.js"); + J2S._coreFiles = [ thisPath ]; } if (more && (Array.isArray(more) || (more = more.split(" ")))) for (var i = 0; i < more.length; i++) - if (more[i] && __coreMore.join("").indexOf(more[i]) < 0) - __coreMore.push(path + "/core/core" + more[i] + ".z.js") + if (more[i] && __coreMore.join("").indexOf(more[i]) < 0) { + isFullName = (more[i].indexOf("/") >= 0); + thisPath = (isFullName ? type : path + "/core/core" + more[i] + ".z.js"); + __coreMore.push(thisPath); + } for (var i = 0; i < __coreMore.length; i++) J2S._coreFiles.push(__coreMore[i]); } @@ -2881,6 +2897,10 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { alert("Java class " + clazz + " was not found."); return; } + var assets = applet.__Info.coreAssets; + if (assets) { + loadAssets(assets); + } if (applet.__Info.code) codePath += applet.__Info.code.replace(/\./g, "/"); codePath = codePath.substring(0, @@ -2889,7 +2909,11 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { applet._codePath = codePath; Clazz.loadClass("java.lang.Thread").currentThread$().group.html5Applet = applet; cl.main$SA(applet.__Info.args || []); - System.exit$(0); + if (applet.__Info.readyFunction) { + J2S._addExec([ this, function(){applet.__Info.readyFunction(applet);}, null, "Info.readyFunction" ]); + } else { + System.exit$(0); + } } else { var viewerOptions = Clazz.new_("java.util.Hashtable"); @@ -2994,6 +3018,27 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { return proto; }; + var loadAssets = function(assets) { + if (!assets) return; + if (typeof assets != "string") { + // assume array + for (var i = 0; i < assets.length; i++) { + loadAssets(assets[i]); + } + return; + } + try { + var bytes = J2S.getFileData(assets,null, true, true); + var bis = Clazz.loadClass("javajs.util.Rdr").getBIS$BA(bytes); + var cache = J2S.getSetJavaFileCache(); + var len0 = cache.size$(); + Clazz.loadClass("javajs.util.ZipTools").readFileAsMap$java_io_BufferedInputStream$java_util_Map$S(bis, cache); + System.out.println((cache.size$() - len0) + " items cached from " + assets); + } catch(e) { + System.out.println("error reading " + assets); + } + } + J2S.repaint = function(applet, asNewThread) { // JmolObjectInterface // asNewThread: true is from RepaintManager.repaintNow() diff --git a/sources/net.sf.j2s.java.core/srcjs/js/j2sClazz.js b/sources/net.sf.j2s.java.core/srcjs/js/j2sClazz.js index c33e5a28b..bbefa820a 100644 --- a/sources/net.sf.j2s.java.core/srcjs/js/j2sClazz.js +++ b/sources/net.sf.j2s.java.core/srcjs/js/j2sClazz.js @@ -7,6 +7,11 @@ // Google closure compiler cannot handle Clazz.new or Clazz.super +// BH 2025.04.17 adds option for explicit directory for core files different from j2sPath/core +// BH 2025.03.12 adds support for writable byte[] parameters in WASM +// BH 2025.03.06 adds support for JNA+WASM, automated loading of Java native classes if WASM is available +// BH 2025.02.22 add hashCode$() for Java Integer.TYPE and related types +// BH 2025.01.31 added checks for JavaScript SyntaxError similar to other Error types // BH 2024.11.23 implementing java.awt.Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval") // BH 2024.06.22 adds Integer.getIngeger(String, int) (returning null) // BH 2024.03.03 removes unnecessary loadClass("xxxx") on exceptionOf(e,"xxxx") call @@ -718,11 +723,198 @@ Clazz.new_ = function(c, args, cl) { return obj; } -// var C$=Clazz.newClass(P$, -// "Test_Local$1", -// function(){Clazz.newInstance(this, arguments[0],1,C$);}, -// Clazz.load('test.Test_Local$1ReducingSink'), null, 1); -// +Clazz._Pointer = null; + +Clazz._loadWasm = function(cls, lib){ + if (cls.wasmLoaded) + return; + cls.wasmLoaded = true; + var libName = lib.getName$(); // "jnainchi" var wasmName = libName + ".wasm"; + if (J2S.wasm && J2S.wasm[libname]) + return; + var className = cls.getName$(); + var classPath = className.substring(0, className.lastIndexOf(".") + 1).replaceAll(".", "/"); + var j2sdir = Thread.秘thisThread.getContextClassLoader$().$_$base; + var libPath = j2sdir + classPath; + var jsClass = cls.$clazz$; + var m; + Clazz._isQuietLoad = true; + var aMethods = cls.getMethods$(); + var nameMap = {}; + var funcs = []; + J2S.wasm || (J2S.wasm = {}); + J2S.wasm[libName] || (J2S.wasm[libName] = {}); + var getFunc = function(cls, newName, sig, ptypes, retType, fargs, fret) { + System.out.println("Clazz.loadWasm creating J2S.wasm." + libName + "." + newName); + return function(module) { + var f = []; + f[0] = module.cwrap(newName, retType, ptypes); + //System.out.println(newName + " " + retType + " " + ptypes); + J2S.wasm[libName][newName] = jsClass[newName] = jsClass[sig] = function() { + var rgs = []; + var ba = []; + var pa = []; + var getNewFunc = false; + for (var i = arguments.length; --i >= 0;) { + var a = arguments[i]; + if (a && a.__NDIM) { + // we have a signature "BA" (byte[]), and the + // developer has passed byte[] rather than the assumed + // String value. So we need to allocate memory for + // the array, fill it, change the wrapping to "number" (for a pointer) + // execute the function, and then retrieve the value. + // Emscripten missed this for some reason, and only reads the array + // but does not re-fill it. I think this is a bug. + ba[i] = a; + pa[i] = module._malloc(a.length); + module.writeArrayToMemory(ba[i], pa[i]); + a = pa[i]; + if (ptypes[i] == "string") { + // now we know that a byte[] is being used at runtime instead of a String, + // we need to recreate the wrapped function. + // this will only happen once. + ptypes[i] = "number"; + getNewFunc = true; + } + } + rgs[i] = (fargs[i] ? fargs[i](a) : a); + } + if (getNewFunc) { + f[0] = module.cwrap(newName, retType, ptypes); + } + var val = f[0].apply(null, rgs); + if (ba.length) { + for (var i = pa.length; --i >= 0;) { + if (pa[i]) { + // fill original array from pointer + for (var pt = pa[i], a = ba[i], n = a.length, j = 0; j < n; j++) { + a[j] = module.getValue(pt++); + } + module._free(pa[i]); + } + } + + } + return (fret ? fret(val, module) : val); + } + } + } + + var argPtr = function(p) { return (p ? p.peer : 0); }; + var retPtr = function(i) { return Clazz.new_(com.sun.jna.Pointer.c$$I, [i]);}; + var retStr = function(p) { if (p < 0) return null;var ret = module.UTF8ToString(p); module._free(p);return ret;}; + var retNull = function() { return null; }; + + for (var i = 0, n = aMethods.length; i < n; i++) { + try { + m = aMethods[i]; + var jsm = m.$meth$; + if (!jsm.isNative) + continue; + var name = m.getName$(); + var newName = name; + var ext = nameMap[name] || 0; + if (ext) { + newName += ext; + } else { + ext++; + } + nameMap[name] = ++ext; + var sig = m.getSignature$(); + var aParams = sig.split("$"); + var ptypes = []; + var fargs = []; + for (var p = 1, np = aParams.length; sig != null && p =0){ TypeError.prototype.getMessage$ || ( - ReferenceError.prototype.getMessage$ = TypeError.prototype.getMessage$ - = ReferenceError.prototype.getMessage$ = TypeError.prototype.getLocalizedMessage$ +SyntaxError.prototype.getMessage$ + = ReferenceError.prototype.getMessage$ + = TypeError.prototype.getMessage$ + = SyntaxError.prototype.getLocalizedMessage$ + = ReferenceError.prototype.getLocalizedMessage$ + = TypeError.prototype.getLocalizedMessage$ = function(){ return (this.stack ? this.stack : this.message || this.toString()) + (this.getStackTrace ? this.getStackTrace$() : Clazz._getStackTrace())}); -TypeError.prototype.getStackTrace$ = ReferenceError.prototype.getStackTrace$ = function() { return Clazz._getStackTrace() } -TypeError.prototype.printStackTrace$ = ReferenceError.prototype.printStackTrace$ = function() { printStackTrace(this,System.err) } -ReferenceError.prototype.printStackTrace$java_io_PrintStream = TypeError.prototype.printStackTrace$java_io_PrintStream = function(stream){stream.println$S(this + "\n" + this.stack);}; -ReferenceError.prototype.printStackTrace$java_io_PrintWriter = TypeError.prototype.printStackTrace$java_io_PrintWriter = function(printer){printer.println$S(this + "\n" + this.stack);}; +TypeError.prototype.getStackTrace$ += SyntaxError.prototype.getStackTrace$ += ReferenceError.prototype.getStackTrace$ += function() { return Clazz._getStackTrace() } +TypeError.prototype.printStackTrace$ += SyntaxError.prototype.printStackTrace$ += ReferenceError.prototype.printStackTrace$ += function() { printStackTrace(this,System.err) } +ReferenceError.prototype.printStackTrace$java_io_PrintStream += TypeError.prototype.printStackTrace$java_io_PrintStream += SyntaxError.prototype.printStackTrace$java_io_PrintStream += function(stream){stream.println$S(this + "\n" + this.stack);}; +ReferenceError.prototype.printStackTrace$java_io_PrintWriter += TypeError.prototype.printStackTrace$java_io_PrintWriter += SyntaxError.prototype.printStackTrace$java_io_PrintWriter += function(printer){printer.println$S(this + "\n" + this.stack);}; Clazz.Error = Error; diff --git a/sources/net.sf.j2s.java.core/srcjs/swingjs2.js b/sources/net.sf.j2s.java.core/srcjs/swingjs2.js index 069aad542..8c4cfd49b 100644 --- a/sources/net.sf.j2s.java.core/srcjs/swingjs2.js +++ b/sources/net.sf.j2s.java.core/srcjs/swingjs2.js @@ -10686,6 +10686,9 @@ return jQuery; })(jQuery,document,"click mousemove mouseup touchmove touchend", "outjsmol"); // j2sApplet.js BH = Bob Hanson hansonr@stolaf.edu +// BH 2025.04.20 adds Info.coreAssets:"coreAssets.zip" +// BH 2025.04.18 enables Info.readyFunction for headless apps +// BH 2025.04.17 adds option for explicit directory for core files different from j2sPath/core // BH 2024.11.09 makes equivalent J2S._debugCore and J2S._nozcore, as well as J2S._debugCode and J2S._nocore // BH 2024.10.03 adds two-finger tap as "click"; reinstates touch gestures lost when we went to pointerup 2023.11.01 // BH 2023.12.14 fixes resizing into application (making it smaller) @@ -13273,26 +13276,39 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { type = null; } + var thisPath, isFullName; if (type) { - type = type.toLowerCase().split(".")[0]; // package name only + var key = type.toLowerCase().substring(type.lastIndexOf("/") + 1); + key = key.toLowerCase().split(".")[0]; // package name only // return if type is already part of the set. - if (__coreSet.join("").indexOf(type) >= 0) + if (__coreSet.join("").indexOf(key) >= 0) return; + __coreSet.push(key); + __coreSet.sort(); + // create a concatenated lower-case name for a core file that // includes // all Java applets on the page + // only if this is not a full name - __coreSet.push(type); - __coreSet.sort(); - J2S._coreFiles = [ path + "/core/core" + __coreSet.join("") - + ".z.js" ]; + // 2025.04.17 adds option to give full (local) path to core file + isFullName = (type.indexOf("/") >= 0); + if (isFullName){ + // bypass core/package.js + J2S.Globals["core.registered"] = true; + } + thisPath = (isFullName ? type : path + "/core/core" + __coreSet.join("") + ".z.js"); + J2S._coreFiles = [ thisPath ]; } if (more && (Array.isArray(more) || (more = more.split(" ")))) for (var i = 0; i < more.length; i++) - if (more[i] && __coreMore.join("").indexOf(more[i]) < 0) - __coreMore.push(path + "/core/core" + more[i] + ".z.js") + if (more[i] && __coreMore.join("").indexOf(more[i]) < 0) { + isFullName = (more[i].indexOf("/") >= 0); + thisPath = (isFullName ? type : path + "/core/core" + more[i] + ".z.js"); + __coreMore.push(thisPath); + } for (var i = 0; i < __coreMore.length; i++) J2S._coreFiles.push(__coreMore[i]); } @@ -13567,6 +13583,10 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { alert("Java class " + clazz + " was not found."); return; } + var assets = applet.__Info.coreAssets; + if (assets) { + loadAssets(assets); + } if (applet.__Info.code) codePath += applet.__Info.code.replace(/\./g, "/"); codePath = codePath.substring(0, @@ -13575,7 +13595,11 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { applet._codePath = codePath; Clazz.loadClass("java.lang.Thread").currentThread$().group.html5Applet = applet; cl.main$SA(applet.__Info.args || []); - System.exit$(0); + if (applet.__Info.readyFunction) { + J2S._addExec([ this, function(){applet.__Info.readyFunction(applet);}, null, "Info.readyFunction" ]); + } else { + System.exit$(0); + } } else { var viewerOptions = Clazz.new_("java.util.Hashtable"); @@ -13680,6 +13704,27 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { return proto; }; + var loadAssets = function(assets) { + if (!assets) return; + if (typeof assets != "string") { + // assume array + for (var i = 0; i < assets.length; i++) { + loadAssets(assets[i]); + } + return; + } + try { + var bytes = J2S.getFileData(assets,null, true, true); + var bis = Clazz.loadClass("javajs.util.Rdr").getBIS$BA(bytes); + var cache = J2S.getSetJavaFileCache(); + var len0 = cache.size$(); + Clazz.loadClass("javajs.util.ZipTools").readFileAsMap$java_io_BufferedInputStream$java_util_Map$S(bis, cache); + System.out.println((cache.size$() - len0) + " items cached from " + assets); + } catch(e) { + System.out.println("error reading " + assets); + } + } + J2S.repaint = function(applet, asNewThread) { // JmolObjectInterface // asNewThread: true is from RepaintManager.repaintNow() @@ -14143,6 +14188,11 @@ if (ev.keyCode == 9 && ev.target["data-focuscomponent"]) { // Google closure compiler cannot handle Clazz.new or Clazz.super +// BH 2025.04.17 adds option for explicit directory for core files different from j2sPath/core +// BH 2025.03.12 adds support for writable byte[] parameters in WASM +// BH 2025.03.06 adds support for JNA+WASM, automated loading of Java native classes if WASM is available +// BH 2025.02.22 add hashCode$() for Java Integer.TYPE and related types +// BH 2025.01.31 added checks for JavaScript SyntaxError similar to other Error types // BH 2024.11.23 implementing java.awt.Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval") // BH 2024.06.22 adds Integer.getIngeger(String, int) (returning null) // BH 2024.03.03 removes unnecessary loadClass("xxxx") on exceptionOf(e,"xxxx") call @@ -14854,11 +14904,198 @@ Clazz.new_ = function(c, args, cl) { return obj; } -// var C$=Clazz.newClass(P$, -// "Test_Local$1", -// function(){Clazz.newInstance(this, arguments[0],1,C$);}, -// Clazz.load('test.Test_Local$1ReducingSink'), null, 1); -// +Clazz._Pointer = null; + +Clazz._loadWasm = function(cls, lib){ + if (cls.wasmLoaded) + return; + cls.wasmLoaded = true; + var libName = lib.getName$(); // "jnainchi" var wasmName = libName + ".wasm"; + if (J2S.wasm && J2S.wasm[libname]) + return; + var className = cls.getName$(); + var classPath = className.substring(0, className.lastIndexOf(".") + 1).replaceAll(".", "/"); + var j2sdir = Thread.秘thisThread.getContextClassLoader$().$_$base; + var libPath = j2sdir + classPath; + var jsClass = cls.$clazz$; + var m; + Clazz._isQuietLoad = true; + var aMethods = cls.getMethods$(); + var nameMap = {}; + var funcs = []; + J2S.wasm || (J2S.wasm = {}); + J2S.wasm[libName] || (J2S.wasm[libName] = {}); + var getFunc = function(cls, newName, sig, ptypes, retType, fargs, fret) { + System.out.println("Clazz.loadWasm creating J2S.wasm." + libName + "." + newName); + return function(module) { + var f = []; + f[0] = module.cwrap(newName, retType, ptypes); + //System.out.println(newName + " " + retType + " " + ptypes); + J2S.wasm[libName][newName] = jsClass[newName] = jsClass[sig] = function() { + var rgs = []; + var ba = []; + var pa = []; + var getNewFunc = false; + for (var i = arguments.length; --i >= 0;) { + var a = arguments[i]; + if (a && a.__NDIM) { + // we have a signature "BA" (byte[]), and the + // developer has passed byte[] rather than the assumed + // String value. So we need to allocate memory for + // the array, fill it, change the wrapping to "number" (for a pointer) + // execute the function, and then retrieve the value. + // Emscripten missed this for some reason, and only reads the array + // but does not re-fill it. I think this is a bug. + ba[i] = a; + pa[i] = module._malloc(a.length); + module.writeArrayToMemory(ba[i], pa[i]); + a = pa[i]; + if (ptypes[i] == "string") { + // now we know that a byte[] is being used at runtime instead of a String, + // we need to recreate the wrapped function. + // this will only happen once. + ptypes[i] = "number"; + getNewFunc = true; + } + } + rgs[i] = (fargs[i] ? fargs[i](a) : a); + } + if (getNewFunc) { + f[0] = module.cwrap(newName, retType, ptypes); + } + var val = f[0].apply(null, rgs); + if (ba.length) { + for (var i = pa.length; --i >= 0;) { + if (pa[i]) { + // fill original array from pointer + for (var pt = pa[i], a = ba[i], n = a.length, j = 0; j < n; j++) { + a[j] = module.getValue(pt++); + } + module._free(pa[i]); + } + } + + } + return (fret ? fret(val, module) : val); + } + } + } + + var argPtr = function(p) { return (p ? p.peer : 0); }; + var retPtr = function(i) { return Clazz.new_(com.sun.jna.Pointer.c$$I, [i]);}; + var retStr = function(p) { if (p < 0) return null;var ret = module.UTF8ToString(p); module._free(p);return ret;}; + var retNull = function() { return null; }; + + for (var i = 0, n = aMethods.length; i < n; i++) { + try { + m = aMethods[i]; + var jsm = m.$meth$; + if (!jsm.isNative) + continue; + var name = m.getName$(); + var newName = name; + var ext = nameMap[name] || 0; + if (ext) { + newName += ext; + } else { + ext++; + } + nameMap[name] = ++ext; + var sig = m.getSignature$(); + var aParams = sig.split("$"); + var ptypes = []; + var fargs = []; + for (var p = 1, np = aParams.length; sig != null && p =0){ TypeError.prototype.getMessage$ || ( - ReferenceError.prototype.getMessage$ = TypeError.prototype.getMessage$ - = ReferenceError.prototype.getMessage$ = TypeError.prototype.getLocalizedMessage$ +SyntaxError.prototype.getMessage$ + = ReferenceError.prototype.getMessage$ + = TypeError.prototype.getMessage$ + = SyntaxError.prototype.getLocalizedMessage$ + = ReferenceError.prototype.getLocalizedMessage$ + = TypeError.prototype.getLocalizedMessage$ = function(){ return (this.stack ? this.stack : this.message || this.toString()) + (this.getStackTrace ? this.getStackTrace$() : Clazz._getStackTrace())}); -TypeError.prototype.getStackTrace$ = ReferenceError.prototype.getStackTrace$ = function() { return Clazz._getStackTrace() } -TypeError.prototype.printStackTrace$ = ReferenceError.prototype.printStackTrace$ = function() { printStackTrace(this,System.err) } -ReferenceError.prototype.printStackTrace$java_io_PrintStream = TypeError.prototype.printStackTrace$java_io_PrintStream = function(stream){stream.println$S(this + "\n" + this.stack);}; -ReferenceError.prototype.printStackTrace$java_io_PrintWriter = TypeError.prototype.printStackTrace$java_io_PrintWriter = function(printer){printer.println$S(this + "\n" + this.stack);}; +TypeError.prototype.getStackTrace$ += SyntaxError.prototype.getStackTrace$ += ReferenceError.prototype.getStackTrace$ += function() { return Clazz._getStackTrace() } +TypeError.prototype.printStackTrace$ += SyntaxError.prototype.printStackTrace$ += ReferenceError.prototype.printStackTrace$ += function() { printStackTrace(this,System.err) } +ReferenceError.prototype.printStackTrace$java_io_PrintStream += TypeError.prototype.printStackTrace$java_io_PrintStream += SyntaxError.prototype.printStackTrace$java_io_PrintStream += function(stream){stream.println$S(this + "\n" + this.stack);}; +ReferenceError.prototype.printStackTrace$java_io_PrintWriter += TypeError.prototype.printStackTrace$java_io_PrintWriter += SyntaxError.prototype.printStackTrace$java_io_PrintWriter += function(printer){printer.println$S(this + "\n" + this.stack);}; Clazz.Error = Error; diff --git a/sources/net.sf.j2s.java.core/resources/_ES6/jsutil.js b/sources/net.sf.j2s.java.core/unused/jsutil.js similarity index 100% rename from sources/net.sf.j2s.java.core/resources/_ES6/jsutil.js rename to sources/net.sf.j2s.java.core/unused/jsutil.js diff --git a/sources/net.sf.j2s.java.core/resources/_WASM/wasi.esm.js b/sources/net.sf.j2s.java.core/unused/wasi.esm.js similarity index 100% rename from sources/net.sf.j2s.java.core/resources/_WASM/wasi.esm.js rename to sources/net.sf.j2s.java.core/unused/wasi.esm.js