andrewisen/bim-whale

Difficult properties to parse

Opened this issue · 2 comments

Some IfcPropertySingleValue that are difficult to parse

'Type',$,IFCLABEL('Table-Night Stand: 24" x 24" x 30"'),$
'URL',$,IFCLABEL('http://www.website.com/anchor#!/800X800-1'),$
'SystemID',$,IFCLABEL('Piping System: KB03R'),$
'TypeDescription',$,IFCTEXT('Circular, smooth,R=D'),$
'AboveGround',$,IFCLOGICAL(.U.),$
'StoreyName',$,IFCTEXT('Level: 02 - Floor'),$
'Diameter',$,IFCPOSITIVELENGTHMEASURE(315.),$
'TypeDescription',$,IFCTEXT('K30, R\X2\00F6\X0\rsk\X2\00E5\X0\l mineralull ytbekl armerad al-folie difft\X2\00E4\X0\t'),$
'Render Appearance Properties',$,IFCTEXT('{2, 15},{39, RPC-16-FXGQ-QK7X-PJ74-6000-ST1L-LVER-AU},{1, 0},{1, 0},{2, 36},{1, 8},{5, Color},{1, 0},{1, 0},{3, 0.8},{3, 0.8},{3, 0.8},{1, 8},{7, BBoxMin},{1, 0},{1, 0},{1, 0},{1, 0},{1, 0},{1, 8},{7, BBoxMax},{1, 0},{1, 0},{1, 0},{1, 0},{1, 0},{2, 11},{9, RPCTypeId},{1, 0},{1, 0},{39, RPC-16-FXGQ-QK7X-PJ74-6000-ST1L-LVER-AU},{1, 6},{14, RPCPlantHeight},{1, 0},{1, 0},{7, 18.0446},{2, 11},{7, keyword},{1, 0},{1, 0},{140, :Geometry:RPC:Golden-chain :Laburnum:Laburnums are very small trees native to southern Europe.  The common cultivar in Canada is ''Watereri''.},{2, 11},{8, category},{1, 0},{1, 0},{16, :Trees [General]},{2, 11},{12, LocalizedUID},{1, 0},{1, 0},{13, Golden-chain },{2, 14},{6, Height},{1, 0},{1, 0},{7, 18.0446},{1, 2},{16, Cast Reflections},{1, 0},{1, 0},{1, 0},{1, 2},{12, Cast Shadows},{1, 0},{1, 0},{1, 1},{1, 2},{9, Lock View},{1, 0},{1, 0},{1, 0},{1, 4},{5, ViewC},{1, 0},{1, 0},{1, 0},{2, 11},{11, RPCFilePath},{1, 0},{1, 0},{99, C:\\RevitRender\\Version2015940\\2017\\assetlibrary_base.fbm\\RPCs\\ArchVision_Stills_T5_Golden_Chain.rpc},{2, 11},{20, AdvancedUIDefinition},{1, 0},{1, 0},{0, },{2, 11},{10, AssetLibID},{1, 0},{1, 0},{36, 4D3F6E72-3F99-4203-98E9-AF80B3C7A7A4},{2, 11},{10, BaseSchema},{1, 0},{1, 0},{9, RPCSchema},{2, 11},{12, ExchangeGUID},{1, 0},{1, 0},{0, },{1, 2},{6, Hidden},{1, 0},{1, 0},{1, 0},{1, 4},{11, RPCCategory},{1, 0},{1, 0},{1, 0},{2, 11},{8, RPCImage},{1, 0},{1, 0},{0, },{1, 4},{13, SchemaVersion},{1, 0},{1, 0},{1, 5},{2, 11},{12, UIDefinition},{1, 0},{1, 0},{0, },{2, 11},{6, UIName},{1, 0},{1, 0},{39, RPC-16-FXGQ-QK7X-PJ74-6000-ST1L-LVER-AU},{2, 11},{11, VersionGUID},{1, 0},{1, 0},{9, RPCSchema},{2, 11},{9, assettype},{1, 0},{1, 0},{11, RPCGeometry},{2, 11},{11, description},{1, 0},{1, 0},{0, },{2, 11},{9, localname},{1, 0},{1, 0},{3, RPC},{2, 11},{9, localtype},{1, 0},{1, 0},{3, RPC},{1, 4},{8, revision},{1, 0},{1, 0},{1, 1},{2, 11},{9, thumbnail},{1, 0},{1, 0},{0, },{1, 4},{7, version},{1, 0},{1, 0},{1, 1},{1, 2},{20, RPC_Cast Reflections},{1, 0},{1, 0},{1, 1},{1, 2},{16, RPC_Cast Shadows},{1, 0},{1, 0},{1, 1},{1, 2},{13, RPC_Lock View},{1, 0},{1, 0},{1, 0},{1, 4},{9, RPC_ViewC},{1, 0},{1, 0},{1, 0},{21, assetlibrary_base.fbx},{1, 5},'),$
 '2moeq0lkL9rx1tiYe2hZKW',#42,'Pipe Insulation:K30:11937246',$,'Pipe Insulation:K30',#1365555,#1367655,'11937246',.INSULATION.

Let's use this as an example:

foo, IFCXXX( YYY ), bar

Also, let's use some JavaScript terminology.
Let's say;

  • IFCXXX is a function
  • YYY is its parameter

A very simple workaround is to:

  1. Remove the parameter from the entire string
  2. Perform the parsing (as normal)
  3. Parse the parameter as well
  4. Add back the parameter to the original string

This works surprisingly well.

var myString = "'Description',$,IFCTEXT('_DESCRIPTION_'),$"

var myStringWithNoParameter = "'Description',$,IFCTEXT({PARAMETER}),$"
var myParameter = myString.getParameter()

var myStringAsJSON = myStringWithOutFunction.parse()
var myParameterAsJSON = myParameter.parser()

var myFinalString =  myStringAsJSON(/{PARAMETER}/, myParameterAsJSON)

See _parseStepInstanceAttributes for more info.

Or, in actual JavaScript

  1. Prepare the variables
var parsedAttributesString = ""; // This is the string we want to return

/**
* Let's "remove" the GlobalId 
*/
var before = ""; // IFC GlobalId
var after = ""; // "Not IFC GlobalId"
  1. Deal with GlobalId (GUIID)
/**
* In my paser; every object that IS NOT an IfcPropertySingleValue is assumed to have a GlobalId
*/
if (entityName !== "IFCPROPERTYSINGLEVALUE") {

        before = attributeString.substr(0, attributeString.indexOf(",")); // Always the first parameter
        after = attributeString.substr(attributeString.indexOf(",")); // The rest of the attributes
        parsedAttributesString = after;
} else {
        parsedAttributesString = attributeString; // The string DOES NOT contain a GlobalId
}
  1. Add commas to make life easier.
    You don't need to write any edge cases for the RegEx
parsedAttributesString = "," + parsedAttributesString + ","; // This will make the parsing a lot easier 
  1. Deal with "functions" and its "parameter"
/**
* Check if the attribute string has a "function"
*/

var hasFunction = false;
var functionParameter = /(IFC[A-Z]+\()(.*)(\)\,)/g.exec(parsedAttributesString);

if (functionParameter) {
        hasFunction = true;
        // Parse the "parameter"
        functionParameter = functionParameter[2]
            .replace(/\\/g, "") // Backward slashes
            .replace(/\//g, "\\/") // Forward slashes
            .replace(/\"/g, '\\"'); // Quotation marks
        parsedAttributesString = parsedAttributesString.replace(
            /(IFC[A-Z]+\()(.*)(\)\,)/g,
            '"$1{PARAM})",'
        );
}
  1. Perform the regular parsing (from a regular string into a JSON object)
// Regular parsing
parsedAttributesString = parsedAttributesString
        .replace(/\\/g, "") // Backward slashes
        .replace(/\//g, "\\/") // Forward slashes
        .replace(/\$/g, '"$"') // Indefinite attributes
        .replace(/(^|,)\((#\d+.*?)\)/g, "$1[$2]") // Nested attributes
        .replace(/([,\[])(#\d+)+/g, '$1"$2"') // References to other entities (e.g. #123)
        .replace(/,(\d+[.]\d*)/g, ",'$1'") // Integers (that are not escaped)
        .replace(/'/g, '"'); // Convert all remaining apostrophes to quotes
  1. Add back the "parameter" (optional)
if (hasFunction) {
        parsedAttributesString = parsedAttributesString.replace(
            /(IFC[A-Z]+\()(\{PARAM\})(\)\"\,)/g,
            "$1" + functionParameter + ')",'
        );
}
  1. Add back the GlobalId (optional)
if (entityName !== "IFCPROPERTYSINGLEVALUE") {
        // Add back the GlobalId
        parsedAttributesString =
            '"' +
            before.slice(1, -1) +
            '"' +
            parsedAttributesString.slice(1, -1); // Remove the added comas
} else {
        parsedAttributesString = parsedAttributesString.slice(1, -1); ; // Remove the added comas
}
  1. Return the parsed string (i.e. a JSON object)
return JSON.parse("[" + parsedAttributesString + "]");