I am returning a list from my Controller as list to view and I am passing this list object to my javascript function like this
window.onload = function() {
showDirectory("$(S3DirectoryList)");
};
S3DirectoryList is the object that is returned from controller.
This is my model class
class Directory {
String folderName;
HashMap< String, String> objects;
List<Directory> dChild;
}
I want to use the folderName property of Directory in function.
Currently my javascript function is defined like this
function showDirectory( dirList) {
var markup="";
<c:forEach var="dir" items="dirList">
markup += ${dir.folderName} + "<br>";
</c:forEach>
document.getElementById("dispdir").innerHTML = markup;
}
I added some code to your Directory class. If you run it(I also added a Main method for testing purposes), you see it creates a list of directories and serializes this list as JSON. I added a constructor to make it easy to create some directories. I also added a getJSON method that serializes a directory. I added a getJSON(List directories) method to serialize a list of directories.
If you see to it this serialized list gets into your variable S3DirectoryList you can pass it to your javascript function as follows:
function showDirectory(dirList) {
var markup = "";
for(var i = 0; i < dirList.length; i++) {
markup += dirList[i].folderName + "<br />";
}
document.getElementById("dispdir").innerHTML = markup;
}
window.onload = function() {
showDirectory($(S3DirectoryList));
};
the Directory class:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Directory{
String folderName;
HashMap<String, String> objects;
List<Directory> dChild;
public Directory(String folderName, String[] objects) {
this.folderName = folderName;
this.objects = new HashMap<String, String>();
for(int i = 0; i < objects.length; i = i + 2) {
this.objects.put(objects[i], objects[i + 1]);
}
this.dChild = new ArrayList<Directory>();
}
public void addChildDirectory(Directory childDirectory) {
if(this.dChild == null)
this.dChild = new ArrayList<Directory>();
this.dChild.add(childDirectory);
}
public String toJSON() {
StringBuilder b = new StringBuilder();
b.append("{");
b.append("'folderName': '").append(folderName == null ? "" : folderName).append("'");
b.append(",objects: {");
if(objects != null) {
Iterator<Map.Entry<String, String>> objectsIterator = objects.entrySet().iterator();
if(objectsIterator.hasNext()) {
Map.Entry<String, String> object = objectsIterator.next();
b.append("'").append(object.getKey()).append("': '").append(object.getValue()).append("'");
}
while (objectsIterator.hasNext()) {
Map.Entry<String, String> object = objectsIterator.next();
b.append(",'").append(object.getKey()).append("': '").append(object.getValue()).append("'");
}
}
b.append("}");
b.append(",'dChild': ");
b.append("[");
if(dChild != null) {
if(dChild.size() > 0)
b.append(dChild.get(0).toJSON());
for(int i = 1; i < dChild.size(); i++) {
b.append(",").append(dChild.get(i).toJSON());
}
}
b.append("]");
b.append("}");
return b.toString();
}
public static String getJSON(List<Directory> directories) {
StringBuilder b = new StringBuilder();
b.append("[");
if(directories.size() > 0)
b.append(directories.get(0).toJSON());
for(int i = 1; i < directories.size(); i++) {
b.append(",").append(directories.get(i).toJSON());
}
b.append("]");
return b.toString();
}
private static Directory generateDirectory(int seed) {
List<Directory> directories = new ArrayList<Directory>();
for(int i = 0; i < 5; i++) {
directories.add(
new Directory(
"folderName_" + seed + "_" + i,
new String[]{"k_" + seed + "_" + i + "_1", "v_" + seed + "_" + i + "_1", "k_" + seed + "_" + i + "_2", "k_" + seed + "_" + i + "_2"}));
}
Directory directory_root = directories.get(0);
Directory directory_1_0 = directories.get(1);
Directory directory_1_1 = directories.get(2);
Directory directory_1_0_0 = directories.get(3);
Directory directory_1_0_1 = directories.get(4);
directory_root.addChildDirectory(directory_1_0);
directory_root.addChildDirectory(directory_1_1);
directory_1_0.addChildDirectory(directory_1_0_0);
directory_1_0.addChildDirectory(directory_1_0_1);
return directory_root;
}
public static void main(String[] args) {
List<Directory> directories = new ArrayList<Directory>();
for(int i = 0; i < 2; i++) {
directories.add(generateDirectory(i));
}
System.out.println(toJSON(directories));
}
}
Related
I want to split a file to blob and upload them to server. I use File.slice() to split file.Then I save these blobs to an array.When i send these blobs to the server, server received files.But the browser prompt an error:
Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'.
Here is my codes.
//html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="file" name="enfile" id="enfile">
<button id="btn">Add</button>
<script>
// ./upload/BlobUpload.do
var btn = document.querySelector('#btn');
btn.onclick = function() {
var src = document.querySelector('#enfile');
var file = src.files[0];
var chunk = 1024 * 100;
var chunks = [];
var start = 0;
var j = 0;
for (var i = 0; i < Math.ceil(file.size / chunk); i++) {
var end = start + chunk;
chunks[i++] = file.slice(start, end);
start = end;
j++;
}
var xhr = new XMLHttpRequest();
xhr.open('post', './upload/BlobUpload.do', true);
for (let i = 0; i < chunks.length; i++) {
var fname = file.name;
var size = file.size;
var formData = new FormData();
formData.append('regularName', fname);
formData.append('size', size);
formData.append('total', j);
formData.append('offset', i * chunk);
formData.append('current', i);
formData.append('data', chunks[i], 'data');
xhr.send(formData);
}
}
var disableCache = function() {
var current = window.location.href;
if (current.indexOf('requestTime') == -1) {
var timestamp = +new Date();
window.location.href = current + '?requestTime=' + timestamp;
}
}
window.addEventListener('load', disableCache);
</script>
</body>
</html>
//servlet
package upload;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.RequestContext;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import utility.FileProperties;
/**
* Servlet implementation class BlobUpload
*/
public class BlobUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
int count = 0;
int current = 0;
int offset = 0;
int size = 0;
HttpSession session;
long timestamp = 0;
String regularName = "";
int total = 0;
FileProperties fileProperties;
/**
* #see HttpServlet#HttpServlet()
*/
public BlobUpload() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String temp = this.getServletContext().getRealPath("/temp/");
String path = this.getServletContext().getRealPath("/attachment/");
String parts = this.getServletContext().getRealPath("/cache/");
File tempDir = new File(temp);
if(!tempDir.exists()) {
tempDir.mkdir();
}
File partsDir = new File(parts);
if(!partsDir.exists()) {
partsDir.mkdir();
}
DiskFileItemFactory dfifBlob = new DiskFileItemFactory();
dfifBlob.setRepository(tempDir);
dfifBlob.setSizeThreshold(1024 * 1024);
RequestContext rcBlob = new ServletRequestContext(request);
ServletFileUpload sfuBlob = new ServletFileUpload(dfifBlob);
sfuBlob.setFileSizeMax(1073741824);
sfuBlob.setSizeMax(1073741824);
List<FileItem> blobPart = sfuBlob.parseRequest(rcBlob);
session = request.getSession(true);
if(session.getAttribute("newFile") == null || ((boolean)session.getAttribute("newFile")) == false) {
session.setAttribute("newFile", true);
timestamp = System.currentTimeMillis();
for(FileItem currentBlob : blobPart) {
if(currentBlob.isFormField()) {
String fieldName = currentBlob.getFieldName();
if(fieldName.equals("total")) {
total = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("current")) {
current = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("offset")) {
offset = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("size")) {
size = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("regularName")) {
regularName = currentBlob.getString("UTF-8");
}
}else {
String cacheDir = parts + timestamp + "/";
File temporary = new File(cacheDir);
if(!temporary.exists()) {
temporary.mkdir();
}
File cache = new File(cacheDir + timestamp + "-" +count + ".bin");
cache.createNewFile();
if(currentBlob.getName() != null && !currentBlob.equals("")) {
InputStream input = currentBlob.getInputStream();
FileOutputStream output = new FileOutputStream(cache);
byte[] buffer = new byte[1024];
int len = 0;
while((len = input.read(buffer)) > 0) {
output.write(buffer, 0, len);
}
output.close();
input.close();
}
}
}
fileProperties = new FileProperties(timestamp, size, regularName, total);
session.setAttribute("properties", fileProperties);
session.setAttribute("fileOffset", offset);
count++;
session.setAttribute("count", count);
if(count == total) {
fileProperties.setClear(true);
fileProperties.setFinish(true);
session.removeAttribute("properties");
session.setAttribute("properties", fileProperties);
}
}else {
fileProperties = (FileProperties)session.getAttribute("filePropertie");
timestamp = fileProperties.getTimestamp();
count = (int)session.getAttribute("count");
for(FileItem currentBlob : blobPart) {
if(currentBlob.isFormField()) {
String fieldName = currentBlob.getFieldName();
if(fieldName.equals("total")) {
total = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("current")) {
current = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("offset")) {
offset = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("size")) {
size = Integer.parseInt(currentBlob.getString("UTF-8"));
}else if(fieldName.equals("regularName")) {
regularName = currentBlob.getString("UTF-8");
}
}else {
String cacheDir = parts + timestamp + "/";
File cache = new File(cacheDir + timestamp + "-" +count + ".bin");
cache.createNewFile();
if(currentBlob.getName() != null && !currentBlob.equals("")) {
InputStream input = currentBlob.getInputStream();
FileOutputStream output = new FileOutputStream(cache);
byte[] buffer = new byte[1024];
int len = 0;
while((len = input.read(buffer)) > 0) {
output.write(buffer, 0, len);
}
output.close();
input.close();
}
}
}
session.setAttribute("fileOffset", offset);
count++;
session.setAttribute("count", count);
if(count == total) {
fileProperties.setClear(true);
fileProperties.setFinish(true);
session.removeAttribute("properties");
session.setAttribute("properties", fileProperties);
}
}
fileProperties = (FileProperties)session.getAttribute("properties");
if(fileProperties.isFinish()) {
File preCombine = new File(parts + fileProperties.getTimestamp() + "/");
File[] combining = preCombine.listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
// TODO Auto-generated method stub
return pathname.getName().endsWith(".bin");
}});
String export = path + fileProperties.getRegularName();
File combined = new File(export);
if(!combined.exists()) {
combined.createNewFile();
}
for(File processing : combining) {
FileInputStream processingInput = new FileInputStream(processing);
FileOutputStream processingOutput = new FileOutputStream(combined,true);
byte[] processingBuffer = new byte[1024];
int len = 0;
while((len = processingInput.read(processingBuffer)) > 0) {
processingOutput.write(processingBuffer, 0, len);
}
processingOutput.close();
processingInput.close();
}
File[] del = preCombine.listFiles();
int j = 0;
for(int i = 0; i < del.length; i++) {
if(del[i].exists()) {
del[i].delete();
}
}
preCombine.delete();
session.removeAttribute("newFile");
session.removeAttribute("count");
session.removeAttribute("properties");
}
}
}
Why the browser prompt the error.File is a blob.
enter image description here
The FileReader interface is part of the W3C File API that is still just a working draft so you can expect it to be inconsistenly implemented across browsers.
ECMA-262 specifies slice as String.prototype.slice(start, end) where start and end reference characters in a string. I expect that if start == end then nothing will be sliced.
The W3C File API working draft specifies File.slice(start, length[, contentType]) where start and length are byte offsets. The optional contentType parameter may allow setting the content to ASCII text or similar.
So it appears that Chrome's File.slice may well be implemented per the ECMA-262 String method rather than the W3C File API method.
I want to export a csv file created in JS and let people download it on their phone.
This is my JS code for creating the file:
var createACSVFile = function () {
var ArrayOfDataToExport = [];
for (var k = 0; k < localStorage.length; k++) {
console.log([localStorage.key(k),JSON.parse(localStorage.getItem(localStorage.key(k)))]);
ArrayOfDataToExport.push([localStorage.key(k),JSON.parse(localStorage.getItem(localStorage.key(k)))])
}
var csvRows = [];
for(var i=0, l=ArrayOfDataToExport.length; i<l; ++i){
csvRows.push(ArrayOfDataToExport[i].join(','));
}
var csvString = csvRows.join("%0A");
var a = document.createElement('a');
a.href = 'data:attachment/csv,' + csvString;
a.target = '_blank';
a.download = 'exportFile.csv';
document.body.appendChild(a);
a.click();
};
createACSVFile();
This is my android code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = (WebView) this.findViewById(R.id.webView);
view.getSettings().setJavaScriptEnabled(true);
view.getSettings().setAllowFileAccess(true);
view.getSettings().setDomStorageEnabled(true);
view.getSettings().setUseWideViewPort(true);
view.getSettings().setLoadWithOverviewMode(true);
view.setInitialScale(1);
view.getSettings().setJavaScriptEnabled(true);
view.getSettings().setSupportZoom(false);
view.setWebViewClient(new MyBrowser(){
#Override
public void onPageFinished(WebView view, String url) {
//hide loading image
findViewById(R.id.imageLoading1).setVisibility(View.GONE);
//show webview
findViewById(R.id.webView).setVisibility(View.VISIBLE);
}
});
view.loadUrl("file:///android_asset/www/index.html");
view.setWebChromeClient(new WebChromeClient(){
});
view.setDownloadListener(new DownloadListener() {
public void onDownloadStart(String url, String userAgent,
String contentDisposition, String mimetype,
long contentLength) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
});
}
I get this error:
08-20 11:12:36.508
17111-17111/checker.coin.crypto.wingcrony.by.cryptocoinchecker
E/AndroidRuntime: FATAL EXCEPTION: main
Process: checker.coin.crypto.wingcrony.by.cryptocoinchecker, PID:
17111
android.content.ActivityNotFoundException: No Activity found to handle
Intent { act=android.intent.action.VIEW
dat=data:attachment/csv,Poloniex,
currencyForToShow,usd
howToOrder,Shortname
passS,false
whichExchangeYouUse,Bitfinex }
at
android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1809)
at
android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
at android.app.Activity.startActivityForResult(Activity.java:3981)
at android.app.Activity.startActivityForResult(Activity.java:3933)
at android.app.Activity.startActivity(Activity.java:4272)
at android.app.Activity.startActivity(Activity.java:4240)
at
checker.coin.crypto.wingcrony.by.cryptocoinchecker.MainActivity$3.onDownloadStart(MainActivity.java:153)
at
com.android.webview.chromium.WebViewContentsClientAdapter.onDownloadStart(WebViewContentsClientAdapter.java:1195)
at
org.chromium.android_webview.AwContentsClientCallbackHelper$MyHandler.handleMessage(AwContentsClientCallbackHelper.java:126)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5728)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
Update
If add this to my androidManifest.xml I get the same error:
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
What I did was I generate the data I need to export to csv in the JS like this:
var generateTheDataToExportToArrayString = function () {
var ArrayOfDataToExport = [];
for (var k = 0; k < localStorage.length; k++) {
var keyName = (localStorage.key(k));
var data = JSON.parse(localStorage.getItem(localStorage.key(k)));
}
var String = '[';
for (var i = 0; i < ArrayOfDataToExport.length; i++){
if (typeof ArrayOfDataToExport[i][1] === "object"){
String += "[" + ArrayOfDataToExport[i][0] + ",";
if (ArrayOfDataToExport[i][1].length > 1 || ArrayOfDataToExport[i][1].length === 0){
if (ArrayOfDataToExport[i][1].length === 0){
String += '[' + '' + ']]';
}
else {
for (var k = 0; k < ArrayOfDataToExport[i][1].length; k++){
String += '[';
for (var l = 0; l < ArrayOfDataToExport[i][1][k].length - 1; l++){
String += ArrayOfDataToExport[i][1][k][l]+ ",";
}
String += ArrayOfDataToExport[i][1][k][ArrayOfDataToExport[i][1][k].length - 1] + "],";
}
String = String.slice(0, -1);
String += "],";
}
}
else {
String += "[";
for (var j = 0; j < ArrayOfDataToExport[i][1].length - 2; j++){
String += ArrayOfDataToExport[i][1][j] + ",";
}
String += ArrayOfDataToExport[i][1][ArrayOfDataToExport[i][1].length - 1] + "]],";
}
}
else {
String += "[" + ArrayOfDataToExport[i][0] + ",";
String += ArrayOfDataToExport[i][1] + "],";
}
}
String = String.slice(0, -1);
String += "]";
console.log(String);
};
You say the last thing I do is log that string. Well that I will capture in the Java code like this:
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
if (sourceID.equals("file:///android_asset/www/assets/js/parseTest.js") && lineNumber == 184 ){
generateNoteOnSD(getBaseContext(),"export.csv", message);
}
}
This is the function generateNoteOnSD:
public void generateNoteOnSD(Context context, String sFileName, String sBody) {
try {
File root = new File(Environment.getExternalStorageDirectory(), "Backup_Data_For_AltFolio");
if (!root.exists()) {
root.mkdirs();
}
File gpxfile = new File(root, sFileName);
FileWriter writer = new FileWriter(gpxfile);
writer.append(sBody);
writer.flush();
writer.close();
Toast.makeText(context, "Saved", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
I need to convert this to c#, but I am not sure how to do it.
getParamsAsStr = function () {
var keys = Object.keys(_self.params ? _self.params : {});
keys.sort();
var response = "";
for (var i = 0 ; i < keys.length ; i++) {
response += _self.params[keys[i]];
}
return response;
}
(Basically, I would love to know what should I use instead of Object.keys)
This function iterates over some object's enumerable properties (Object.keys) and writes out the property values to a string - though without the keys and without any delimiters.
I don't know what _self.params refers to in this context as it isn't a JavaScript intrinsic nor have you provided its definition.
A direct translation to C# is not possible as C#/.NET do not use prototypes with enumerable properties, the closest analogue is to represent _self.params as a Dictionary<Object,String>:
public static String GetParamsAsStr(Dictionary<Object,String> p) {
if( p == null || p.Count == 0 ) return String.Empty;
StringBuilder sb = new StringBuilder();
foreach(Object key in p.Keys) sb.Append( p[key] );
return sb.ToString();
}
I am writing this as an answer to be able to place the whole thing...
This is the original JS code which sets the first parameter to make, later on, some API calls:
var Signer = function () {
this.apkId = getApkId();
this.apkSecret = getApkSecret();
this.servicio = "";
this.sessionToken = "";
this.timestamp = "";
this.requestId = "";
this.params = "";
var _self = this;
this.getParamsAsStr = function () {
var keys = Object.keys(_self.params ? _self.params : {});
keys.sort();
var response = "";
for (var i = 0 ; i < keys.length ; i++) {
response += _self.params[keys[i]];
}
return response;
}
this.getSignature = function () {
var baseString =
_self.apkSecret +
_self.servicio +
_self.sessionToken +
_self.timestamp +
_self.requestId +
_self.getParamsAsStr();
console.log("Signature pre hash:\n" + baseString);
baseString = baseString.toLowerCase();
return sha1(baseString);
}
}
And, so far, what I did in C# is the following:
public class Signer
{
public string appId = getApkId();
public string appSecret = getAppSecret();
public string servicio = "";
public string sessionToken = "";
public string timestamp = "";
public string requestId = "";
public string params = "";
//Here I have to write the getParamsAsStr()
private static string getApkId(){
string id = "xxxxxxxxxxxxxxxx";
return id;
}
private static string getAppSecret(){
string id = "xxxxxxxxxxxxxxxx";
return id;
}
}
I am using the code below to display an uploaded picture in asp.net . The Image name is change to a unique name and args.get_fileName retrieves the original name of the file instead of the new unique name.
How can I retrieve the new unique name:
protected void FileUploadComplete(object sender, EventArgs e)
{
var date = DateTime.Now.ToString("yyyy-MM-dd-hh_mm_ss");
string filename = System.IO.Path.GetFileName(AsyncFileUpload1.FileName);
AsyncFileUpload1.SaveAs("~/listingImages/" + date + filename);
}
Script:
function uploadStarted() {
$get("imgDisplay").style.display = "none";
}
function uploadComplete(sender, args) {
var imgDisplay = $get("imgDisplay");
imgDisplay.src = "images/loader.gif";
imgDisplay.style.cssText = "";
var img = new Image();
img.onload = function () {
imgDisplay.style.cssText = "height:100px;width:100px";
imgDisplay.src = img.src;
};
img.src = "/listingImages/" + args.get_fileName();
$('#bodyContent_Label1').html("/listingImages/" + args.get_fileName());
}
You should use password generating concept
private static string CreateRandomPassword(int passwordLength)
{
string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!#$?_-";
char[] chars = new char[passwordLength];
Random rd = new Random();
for (int i = 0; i < passwordLength; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
In mustache/handlerbars or any templating system in js,
are there any tools to gather all the variables that a template refers to?
for example, given input
templateHTML:
<div class="{{divclass}}">
{#item}}
<h1>{{title}}</h1>
<span>{{text}}</span>
{{/item}}
</div>
would return
{
divclass: "",
item: {
title: "",
text: ""
}
}
when used like MyTemplateTool.getTemplateData(templateHTML)
I tried googling around and checking out handlebars docs but could not find anything suitable
I believe it's just .. See the related docs on paths. Example:
{{#srcs}}
<script src="{{.}}"></script>
{{/srcs}}
I had the same in mind and I thought it was worth while writing something so here you go:
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MustacheSchemaBuilder
{
static String javaIdentifierRegExp = "[a-zA-Z_$0-9]+";
static String tab = " ";
static Format format = Format.java;
enum Format {
json,
java,
;
}
enum TagType {
variable("\\{\\{\\s*" + javaIdentifierRegExp + "\\s*\\}\\}"),
escapedVariable("\\{\\{\\{\\s*" + javaIdentifierRegExp + "\\s*\\}\\}\\}"),
sectionStart("\\{\\{#\\s*" + javaIdentifierRegExp + "\\s*\\}\\}"),
invertedSectionStart("\\{\\{\\^\\s*" + javaIdentifierRegExp + "\\s*\\}\\}"),
sectionEnd("\\{\\{/\\s*" + javaIdentifierRegExp + "\\s*\\}\\}"),
//partial("\\{\\{>\\s*" + javaIdentifierRegExp + "\\s*\\}\\}"),
// no support for set delimiters or partials
;
Pattern pat;
private TagType(String aRegExp) {
pat = Pattern.compile(aRegExp);
}
public Pattern getPattern() { return pat; };
}
class Tag{
public TagType type;
public String identifier;
Tag(TagType aType, String aIdentifier) { type = aType; identifier = aIdentifier;}
public String toString() { return type.name() + " : " + identifier; }
#Override
public boolean equals(Object aOther) {
if(!(aOther instanceof Tag)) return false;
Tag ot = (Tag) aOther;
return ot.type.equals(this.type) && ot.identifier.equals(this.identifier);
}
#Override
public int hashCode() {return identifier.hashCode();}
}
class Node {
public Section parent;
public Tag tag;
Node(Section aParent, Tag aTag){parent = aParent; tag = aTag; if(parent != null) parent.children.put(tag, this);}
#Override
public int hashCode() {return tag.hashCode();}
public int depth() {
int depth = 0;
Node currentNode = this;
while(currentNode.parent != null) {
depth++;
currentNode = currentNode.parent;
}
return depth;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < this.depth(); i++) sb.append(tab);
switch(format) {
case java:
sb.append("public Object ").append(tag.identifier).append(";\n");
break;
case json:
default:
sb.append(tag.identifier).append(": {},\n");
}
return sb.toString();
}
}
class Section extends Node{
public Map<Tag, Node> children = new LinkedHashMap();
Section(Section aParent, Tag aTag) { super(aParent, aTag); };
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < this.depth(); i++) sb.append(tab);
switch(format) {
case java:
sb.append("public Object ").append(tag.identifier).append(" = new Object() {\n");
for(Node child : this.children.values()) sb.append(child);
for(int i = 0; i < this.depth(); i++) sb.append(tab);
sb.append("};\n");
break;
case json:
default:
sb.append(tag.identifier).append(": {\n");
for(Node child : this.children.values()) sb.append(child);
for(int i = 0; i < this.depth(); i++) sb.append(tab);
sb.append("},\n");
}
return sb.toString();
}
}
class MustacheSchemaBuildException extends Exception {
private static final long serialVersionUID = 1L;
MustacheSchemaBuildException(String aMessage) {super(aMessage);}
}
Section rootSection;
public MustacheSchemaBuilder(String aContents) throws Exception
{
TreeMap<Integer, Tag> tagMap = new TreeMap();
for(TagType type : TagType.values()) {
populateMap(tagMap, type, aContents);
}
System.out.println(tagMap);
rootSection = new Section(null, new Tag(TagType.sectionStart, "$root$"));
Section currentSection = rootSection;
for(Tag tag : tagMap.values()) {
if(currentSection.tag.type == TagType.invertedSectionStart) {
if(tag.type == TagType.sectionEnd) {
if(!tag.identifier.equals(currentSection.tag.identifier))
throw new MustacheSchemaBuildException("The end tag: " + tag.identifier + " doesn't match the expected start tag: " + currentSection.tag.identifier + "\n\n" + rootSection);
currentSection = currentSection.parent;
}
continue;
}
switch(tag.type)
{
case variable:
if(!currentSection.children.containsKey(tag))
new Node(currentSection, tag);
break;
case sectionStart:
Tag invertedSectionStartTag = new Tag(TagType.invertedSectionStart, tag.identifier);
if(!(currentSection.children.containsKey(tag) || currentSection.children.containsKey(invertedSectionStartTag)))
new Section(currentSection, tag);
currentSection = (Section)currentSection.children.get(tag);
break;
case invertedSectionStart:
Tag sectionStartTag = new Tag(TagType.sectionStart, tag.identifier);
if(!(currentSection.children.containsKey(tag) || currentSection.children.containsKey(sectionStartTag)))
new Section(currentSection, tag);
currentSection = (Section)currentSection.children.get(sectionStartTag);
break;
case sectionEnd:
if(!tag.identifier.equals(currentSection.tag.identifier))
throw new MustacheSchemaBuildException("The end tag: " + tag.identifier + " doesn't match the expected start tag: " + currentSection.tag.identifier + "\n\n" + rootSection);
currentSection = currentSection.parent;
break;
default:
}
}
}
public void build() {
System.out.println(rootSection);
}
public static void main(String[] args) throws Exception
{
String contents;
try {
contents = readFile(args[0], Charset.defaultCharset());
}catch(Exception e)
{
System.out.println("Unable to open file!");
throw e;
}
MustacheSchemaBuilder builder = new MustacheSchemaBuilder(contents);
builder.build();
}
void populateMap(Map<Integer, Tag> map, TagType aType, String aContents) {
Matcher m = aType.getPattern().matcher(aContents);
while(m.find()) {
int start = m.start() + 3;
if(aType == TagType.variable) start = m.start() + 2;
map.put(m.start(), new Tag(aType, aContents.substring(start, m.end() - 2).trim()));
}
}
static String readFile(String path, Charset encoding) throws IOException
{
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
}
Give the template file path at the command line - you need java 7 or higher to open the file, or you can just rewrite the readFile - method. You can produce a json or java 'schema' by changing the format at the top of the class. The 'schema' gets written to stdout, you can copy it from there and paste it into where you need it. Hope this helps. Ah! This doesn't support partials or set delimiters, but if you get round to writing support ping it to me as I might want it :)