[x3d-public] setUSE in X3DJSAIL

John Carlson yottzumm at gmail.com
Thu Jul 20 13:33:27 PDT 2017


Of note, I believe an X3DJSAIL program can serialize itself out to Java source.   I am not sure if the result will be valid or not. It’s an important step forward however, I believe.  I had a version that went to ES5, but that has fallen into disrepair, if anyone wants to pick it up again. I use my JSON -> DOM -> JavaScript serializer which is much easier for me to maintain.

Would love to see more people jump on the bandwagon. We need JavaScript, C++/X# and Python versions of X3DJSAIL—Maybe you can convert your favorite X3D Browser into a library!  Apparently converting from Java doesn’t cut it yet, but it could get us quite a ways.

John

Sent from Mail for Windows 10

From: Don Brutzman
Sent: Thursday, July 20, 2017 3:51 PM
To: Roy Walmsley; 'John Carlson'
Cc: X3D Graphics public mailing list
Subject: Re: setUSE in X3DJSAIL

Yes, X3DJSAIL ensures that any X3D model (or scene, or subtree) has sufficient information to serialize itself out in any of the various file encodings (XML, ClassicVRML, JSON).  TODO still: .x3db and .exi.

To do this, the programmer has to be able to set values for DEF and USE in the constructed X3D object tree, just as if creating a file.

Hence setDEF()/getDEF() and setUSE()/getUSE() methods.

Example usage excerpts and result:

==================================================
http://www.web3d.org/x3d/stylesheets/java/examples/HelloWorldProgram.java

String          worldInfoDEFname = "WorldInfoDEF";
WorldInfoObject   worldInfoNode  = new WorldInfoObject(worldInfoDEFname);
WorldInfoObject   worldInfoCopy1 = new WorldInfoObject();
WorldInfoObject   worldInfoCopy2 = new WorldInfoObject();
// [...]

worldInfoNode.setTitle ("HelloWorldProgram produced by X3D Java SAI Library (X3DJSAIL)");
worldInfoCopy1.setUSE(worldInfoDEFname); // setUSE via string
worldInfoCopy2.setUSE(worldInfoNode);    // setUSE via node
//	worldInfoCopy2.addComments("test exception at runtime"); // test sat: cannot add content to USE node
scene.addChildren(worldInfoNode);
scene.addChildren(worldInfoCopy1);
scene.addChildren(worldInfoCopy2);

==================================================
http://www.web3d.org/specifications/java/examples/HelloWorldProgramOutput.x3d

<WorldInfo DEF='WorldInfoDEF' title='HelloWorldProgram produced by X3D Java SAI Library (X3DJSAIL)'/>
<WorldInfo USE='WorldInfoDEF'/>
<WorldInfo USE='WorldInfoDEF'/>
==================================================

Furthermore, the supporting methods for each element class includes checks when invoking setDEF(String) and setUSE(String) as well as a validate() method so that a programmer cannot create a broken node (such as containing both DEF and USE simultaneously.  The validate() method also reports if missing a DEF corresponding to a USE value.

This supports X3DJSAIL goals for making it easy to create an X3D scene graph using Java, while also making it hard to create an incorrect or invalid X3D model.

The following excerpt from autogenerated codes show how this is accomplished inside the X3DJSAIL library. WorldInfoObject.java attached.

==================================================
/**
  * Assign String value to inputOutput SFString field named <i>DEF</i>.
  * <br><br>
  * <i>Tooltip:</i> DEF defines a unique ID name for this node, referenceable by other nodes. Hint: descriptive DEF names improve clarity and help document a model. Hint: well-defined names can simplify design and debugging through improved author understanding. Hint: X3D Scene Authoring Hints, Naming Conventions http://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html#NamingConventions.
  * <br><br>
  *  Note that setting the DEF value clears the USE value.
  * @param newValue is new value for the DEF field.
  * @return {@link WorldInfoObject} - namely <i>this</i> same object to allow sequential method pipelining (i.e. consecutive method invocations on the same node object).
  */
@Override
public final WorldInfoObject setDEF(String newValue)
{

	if (newValue == null)
		newValue = new String();
	// Check that newValue parameter meets naming requirements before assigning to WorldInfo
	if (!newValue.isEmpty() && !org.web3d.x3d.jsail.fields.SFStringObject.isNMTOKEN(newValue))
	{
		throw new org.web3d.x3d.sai.InvalidFieldValueException("WorldInfo DEF newValue='" + newValue + "'" +
			" has illegal name value, cannot be empty and must be defined with valid NMTOKEN name string" +
			" (with legal characters and no embedded whitespace).");
	}
	setConcreteUSE(""); // ensure that no previous USE value remains
	setConcreteDEF(newValue); // private superclass methods
	return this;
}
==================================================
/**
  * Assign String value to inputOutput SFString field named <i>USE</i>.
  * <br><br>
  * <i>Tooltip:</i> USE means reuse an already DEF-ed node ID, excluding all child nodes and all other attributes (except for containerField, which can have a different value). Hint: USE references to previously defined DEF geometry (instead of duplicating nodes) can improve performance. Warning: do NOT include any child nodes, a DEF attribute, or any other attribute values (except for containerField) when defining a USE attribute. Warning: each USE value must match a corresponding DEF value that is defined earlier in the scene.
  * <br><br>
  * <i>Note:</i> each <code>USE</code> node is still an independent object, with the <code>USE</code> value matching the <code>DEF</code> value in the preceding object.
  * <br><br>
  * <i>WARNING:</i> invoking the <code>setUSE()</code> method resets all other fields to their default values and also releases all child nodes.<br><br>
  * <i>WARNING:</i> no other operations can be performed to modify a USE node other than setting an alternate containerField value.
  * @param newValue is new value for the USE field.
  * @return {@link WorldInfoObject} - namely <i>this</i> same object to allow sequential method pipelining (i.e. consecutive method invocations on the same node object).
  */
@Override
public final WorldInfoObject setUSE(String newValue)
{

	if (newValue == null)
		newValue = new String();
	// Check that newValue parameter meets naming requirements before assigning to WorldInfo
	if (!newValue.isEmpty() && !org.web3d.x3d.jsail.fields.SFStringObject.isNMTOKEN(newValue))
	{
		throw new org.web3d.x3d.sai.InvalidFieldValueException("WorldInfo USE newValue='" + newValue + "'" +
			" has illegal name value, cannot be empty and must be defined with valid NMTOKEN name string" +
			" (with legal characters and no embedded whitespace).");
	}
	initialize(); // reset all other field values to default (equivalent to empty)
	setConcreteUSE(newValue); // private superclass method
	return this;
}
==================================================
/**
  * Recursive method to validate this element plus all contained nodes and statements.
  * @return validation results (if any)
  */
@Override
public String validate()
{
	validationResult = new StringBuilder(); // prepare for updated results

	setInfo(getInfo()); // exercise field checks, simple types

	setTitle(getTitle()); // exercise field checks, simple types

	if (!isUSE()) // be careful! setting DEF via setDEF() method will reset USE value
		setDEF(getDEF()); // exercise field checks, simple types

	if (isUSE()) // be careful! setting USE via setUSE() method resets all attributes to default values and wipes out all children
		setUSE(getUSE()); // exercise field checks, simple types

	setCssClass(getCssClass()); // exercise field checks, simple types

	if (metadata != null)
	{
		setMetadata(getMetadata());
		((X3DConcreteElement) metadata).validate(); // exercise field checks, SFNode
		validationResult.append(((X3DConcreteElement) metadata).getValidationResult());
	}
	if (metadataProtoInstance != null)
	{
		setMetadata(getMetadataProtoInstance());
		((X3DConcreteElement) metadataProtoInstance).validate(); // exercise field checks, SFNode
		validationResult.append(((X3DConcreteElement) metadataProtoInstance).getValidationResult());
	}
	if ((metadata != null) && (metadataProtoInstance != null))
	{
		String errorNotice = "Internal X3DJSAIL error: incorrect handling of contained SFNode field, both metadata and metadataProtoInstance are set simultaneously";
		validationResult.append(errorNotice);
		throw new InvalidProtoException(errorNotice); // report error
	}
	if (isUSE() && hasMetadata()) // test USE restrictions
	{
		String errorNotice = "WorldInfo USE='" + getUSE() + "' is not allowed to have contained SFNode metadata";
		validationResult.append(errorNotice);
		throw new InvalidFieldValueException(errorNotice); // report error		
	}

	if (isUSE() && !commentsList.isEmpty())// test USE restrictions
	{
		String errorNotice = "WorldInfo USE='" + getUSE() + "' is not allowed to have contained comments";
		validationResult.append(errorNotice);
		throw new InvalidFieldValueException(errorNotice); // report error		
	}

	if (getIS() != null)
	{
		if (getIS().getConnectList().isEmpty())
		{
			String errorNotice = "IS statement present, but contains no connect statements";
			validationResult.append(errorNotice).append("\n");
			throw new InvalidProtoException(errorNotice); // report error
		}				
		// TODO also check that this node has ancestor ProtoBody, and that a field with same name also exists, so that IS is legal
	}
	if (!getContainerFieldOverride().isEmpty() &&
		!Arrays.asList(containerField_ALTERNATE_VALUES).contains(getContainerFieldOverride()))
	{
		String errorNotice = ConfigurationProperties.ERROR_ILLEGAL_VALUE +
			": illegal value enountered, containerField='" + getContainerFieldOverride() +
			"' but allowed values are containerField_ALTERNATE_VALUES='" +
			new MFStringObject(containerField_ALTERNATE_VALUES).toStringX3D() + "'.";
		validationResult.append(errorNotice).append("\n");
		throw new InvalidFieldException(errorNotice); // report error
	}
	return validationResult.toString();
}
==================================================



On 7/20/2017 7:29 AM, Roy Walmsley wrote:
> Don and John,
> 
> If we are talking about the step of writing our a file, then this is a different question.
> 
> We can use the SAI methods to query the scene in memory. Methods to actually write the scene are a function of the implementation, and not a formal part of the SAI. So, while an implementation may have a setUSE method as part of the writing out interfaces, it is not a formal SAI method. It would be appropriate to indicate, perhaps in comments, those methods that are provided for the convenience of code that outputs the scene to a specific encoding.
> 
> For example, I could consider the following partial algorithm for writing out a file:
> 
>  1. Traverse the scene, stepping through each node, using depth first ordering
>  2. For each node:
>      1. Check if this node has already been written
>      2. If not, write out this node in full
>      3. If so, only write this node with a USE atttrbute.
> 
> Thus, this function would only write out the node type and the USE attribute for step 2)c. And there would be no “resetting of values” involved.
> 
> All the best,
> 
> Roy
> 
> *From:*John Carlson [mailto:yottzumm at gmail.com]
> *Sent:* 20 July 2017 15:06
> *To:* Roy Walmsley <roy.walmsley at ntlworld.com>; 'Don Brutzman' <brutzman at nps.edu>
> *Cc:* 'list' <x3d-public at web3d.org>
> *Subject:* RE: setUSE in X3DJSAIL
> 
> Roy,
> 
> I imagine setUSE was chosen because it was either an attribute in XML or property @USE in JSON, and we were translating XML and JSON to Java, Nashorn JavaScript and Python. Please provide an equivalent conversion of an X3D XML file to C,C++, or C# which does not use setUSE and uses SAI as you suggest. If you can do it, we will copy you.
> 
> We would like to use chained methods with a minimum of extra variables (ideally, just one variable for whole scenegraph will do—not currently achieved). In addition, we would like to take advantage of type safety as much as possible.  Please consider type safety when writing your code.
> 
> There are DOM -> various languages examples you can find in X3DJSONLD/src/main/node.  JavaScriptSerializer.js and JavaSerializer.js are good ones.  You can use any language you want, but if you integrate with X3DJSONLD (json2all.js), it will be integrated into my test suite.
> 
> It is also possible that setUSE was required in the concrete classes (not SAI that I know of) and extracting setUSE up to the interface was a good idea.  I think this can be solved by providing an intermediate interface.
> 
> Thanks,
> 
> John
> 
> Sent from Mail <https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10
> 
> *From: *Roy Walmsley <mailto:roy.walmsley at ntlworld.com>
> *Sent: *Thursday, July 20, 2017 9:13 AM
> *To: *'Don Brutzman' <mailto:brutzman at nps.edu>
> *Cc: *'John Carlson' <mailto:yottzumm at gmail.com>; 'list' <mailto:x3d-public at web3d.org>
> *Subject: *setUSE in X3DJSAIL
> 
> Hi Don,
> 
> I note that in X3DJSAIL you have a “setUSE” method defined in all node classes. I don’t see any point of having this method.
> 
> My thinking is as follows. Please correct it as necessary.
> 
> X3DJSAIL is meant to be an interface for an implementation of the Java Language binding – ISO/IEC 19777-2.
> 
> The SAI 19775-2 has no service related to “USE”. So it does not appear in the Java language binding.
> 
> Consider that an instance of a node (say Material) has been  loaded into memory and added into a scene. Then consider that further down in the scene it is desirable to implement a “USE”. What are the steps?
> 
> The first would be to call the Java implementation of the SAI execution context service “getNode”, with the SAIString parameter set to the DEF name, and the SAIAction parameter set to DEFNode.  In the standard ISO/IEC 19777-2 V3.0, clause 6.4.6 getNode the defined method is getNamedNode. What is your equivalent function in X3DJSAIL?
> 
> Having obtained a node reference for the DEF’d node, let’s assume you also have a node reference for the node that is to be the parent of the USE node, which I will assume for this example will be Appearance.
> 
> You now need to obtain a reference for the “material” field of the Appearance node.  We need to  use the SAI service getField, defined in 6.6.4 getField in ISO/IEC 19775-2. The Java language binding lists this as getField in clause 6.6.4 of ISO/IEC 19777-2. What is your equivalent function in X3DJSAIL?
> 
> Now that we have the field reference, and the node reference to add, we can finally make a call to the SAI service setValue, defined in 6.7.6 setValue in ISO/IEC 19775-2. The Java language binding lists this as setValue in 6.7.6 of ISO/IEC 19777-2. What is your equivalent function in X3DJSAIL?
> 
> Final comments relate to garbage management. The implementation will probably want some way to remember that the same node reference has been used in multiple places. That is something internal to the implementation, and not anything for the interface.
> 
> So, in conclusion, I don’t see a need for your setUSE method. And it is not in the SAI.
> 
> All the best,
> 
> Roy
> 


all the best, Don
-- 
Don Brutzman  Naval Postgraduate School, Code USW/Br       brutzman at nps.edu
Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA   +1.831.656.2149
X3D graphics, virtual worlds, navy robotics http://faculty.nps.edu/brutzman

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://web3d.org/pipermail/x3d-public_web3d.org/attachments/20170720/4449b3af/attachment-0001.html>


More information about the x3d-public mailing list