Files
MapLibreParticleLab/platform/android/scripts/generate-style-code.js
2016-10-25 13:52:36 -07:00

283 lines
10 KiB
JavaScript

'use strict';
const fs = require('fs');
const ejs = require('ejs');
const spec = require('mapbox-gl-style-spec').latest;
const _ = require('lodash');
// Specification parsing //
//Collect layer types from spec
const layers = Object.keys(spec.layer.type.values).map((type) => {
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
if (name !== 'visibility') {
spec[`layout_${type}`][name].name = name;
memo.push(spec[`layout_${type}`][name]);
}
return memo;
}, []);
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
return memo;
}, []);
return {
type: type,
doc: spec.layer.type.values[type].doc,
layoutProperties: layoutProperties,
paintProperties: paintProperties,
properties: layoutProperties.concat(paintProperties)
};
});
//Process all layer properties
const layoutProperties = _(layers).map('layoutProperties').flatten().value();
const paintProperties = _(layers).map('paintProperties').flatten().value();
const allProperties = _(layoutProperties).union(paintProperties).value();
const enumProperties = _(allProperties).filter({'type': 'enum'}).value();
// Global functions //
global.iff = function (condition, val) {
return condition() ? val : "";
}
global.camelize = function (str) {
return str.replace(/(?:^|-)(.)/g, function (_, x) {
return x.toUpperCase();
});
}
global.camelizeWithLeadingLowercase = function (str) {
return str.replace(/-(.)/g, function (_, x) {
return x.toUpperCase();
});
}
global.snakeCaseUpper = function snakeCaseUpper(str) {
return str.replace(/-/g, "_").toUpperCase();
}
global.propertyType = function propertyType(property) {
switch (property.type) {
case 'boolean':
return 'Boolean';
case 'number':
return 'Float';
case 'string':
return 'String';
case 'enum':
return 'String';
case 'color':
return 'String';
case 'array':
return `${propertyType({type:property.value})}[]`;
default:
throw new Error(`unknown type for ${property.name}`);
}
}
global.propertyJNIType = function propertyJNIType(property) {
switch (property.type) {
case 'boolean':
return 'jboolean';
case 'jfloat':
return 'Float';
case 'String':
return 'String';
case 'enum':
return 'String';
case 'color':
return 'String';
case 'array':
return `jarray<${propertyType({type:property.value})}[]>`;
default:
return 'jobject*';
}
}
global.propertyNativeType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
switch (property.type) {
case 'boolean':
return 'bool';
case 'number':
return 'float';
case 'string':
return 'std::string';
case 'enum':
return `${camelize(property.name)}Type`;
case 'color':
return `Color`;
case 'array':
if (property.length) {
return `std::array<${propertyType({type: property.value})}, ${property.length}>`;
} else {
return `std::vector<${propertyType({type: property.value})}>`;
}
default: throw new Error(`unknown type for ${property.name}`)
}
}
global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
switch (property.type) {
case 'enum':
return `@Property.${snakeCaseUpper(property.name)}`;
default:
return "";
}
};
global.defaultValueJava = function(property) {
if(property.name.endsWith("-pattern")) {
return '"pedestrian-polygon"';
}
if(property.name.endsWith("-font")) {
return 'new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}';
}
switch (property.type) {
case 'boolean':
return 'true';
case 'number':
return '0.3f';
case 'string':
return '"' + property['default'] + '"';
case 'enum':
return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]);
case 'color':
return '"rgba(0, 0, 0, 1)"';
case 'array':
switch (property.value) {
case 'string':
return '[' + property['default'] + "]";
case 'number':
var result ='new Float[]{';
for (var i = 0; i < property.length; i++) {
result += "0f";
if (i +1 != property.length) {
result += ",";
}
}
return result + "}";
}
default: throw new Error(`unknown type for ${property.name}`)
}
}
/**
* Produces documentation for property factory methods
*/
global.propertyFactoryMethodDoc = function (property) {
let doc = property.doc;
//Match other items in back ticks
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0] && _(enumProperties).filter({'name': symbol}).value().length > 0) {
//Property 'enums'
symbol = snakeCaseUpper(symbol);
return '{@link Property.' + symbol + '}';
} else if( _(allProperties).filter({'name': symbol}).value().length > 0) {
//Other properties
return '{@link PropertyFactory#' + camelizeWithLeadingLowercase(symbol) + '}';
} else {
//Left overs
return '`' + symbol + '`';
}
});
return doc;
};
/**
* Produces documentation for property value constants
*/
global.propertyValueDoc = function (property, value) {
// Match references to other property names & values.
// Requires the format 'When `foo` is set to `bar`,'.
let doc = property.values[value].doc.replace(/When `(.+?)` is set to `(.+?)`,/g, function (m, peerPropertyName, propertyValue, offset, str) {
let otherProperty = snakeCaseUpper(peerPropertyName);
let otherValue = snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(propertyValue);
return 'When {@link ' + `${otherProperty}` + '} is set to {@link Property#' + `${otherValue}` + '},';
});
// Match references to our own property values.
// Requires the format 'is equivalent to `bar`'.
doc = doc.replace(/is equivalent to `(.+?)`/g, function(m, propertyValue, offset, str) {
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(propertyValue);
return 'is equivalent to {@link Property#' + propertyValue + '}';
});
//Match other items in back ticks
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) {
//Property values
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(symbol);
console.log("Transforming", symbol, propertyValue);
return '{@link Property#' + `${propertyValue}` + '}';
} else if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0]) {
//Property 'enums'
symbol = snakeCaseUpper(symbol);
return '{@link ' + symbol + '}';
} else {
//Left overs
return symbol
}
});
return doc;
};
// Template processing //
// Java + JNI Layers (Peer model)
const layerHpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
const layerCpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
const layerJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs', 'utf8'), {strict: true});
const layerJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs', 'utf8'), {strict: true});
for (const layer of layers) {
fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.hpp`, layerHpp(layer));
fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.cpp`, layerCpp(layer));
fs.writeFileSync(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/${camelize(layer.type)}Layer.java`, layerJava(layer));
fs.writeFileSync(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer));
}
// Java PropertyFactory
const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs', 'utf8'), {strict: true});
fs.writeFileSync(
`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java`,
propertiesTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties})
);
// Java Property
const enumPropertyJavaTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs', 'utf8'), {strict: true});
fs.writeFileSync(
`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`,
enumPropertyJavaTemplate({properties: enumProperties})
);
//De-duplicate enum properties before processing jni property templates
const enumPropertiesDeDup = _(enumProperties).uniqBy(global.propertyNativeType).value();
// JNI Enum property conversion templates
const enumPropertyHppTypeStringValueTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types_string_values.hpp.ejs', 'utf8'), {strict: true});
fs.writeFileSync(
`platform/android/src/style/conversion/types_string_values.hpp`,
enumPropertyHppTypeStringValueTemplate({properties: enumPropertiesDeDup})
);
// JNI property value types conversion templates
const enumPropertyHppTypeTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types.hpp.ejs', 'utf8'), {strict: true});
fs.writeFileSync(
`platform/android/src/style/conversion/types.hpp`,
enumPropertyHppTypeTemplate({properties: enumPropertiesDeDup})
);