Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Would that be the right approach? If so, how should I implement it in Java?
An elegant way of doing this would be to implement an XMLReader that can read the CSV file line by line (using a BufferedReader) to output the corresponding SAX events. Then use this CSV parser and the generated events as a stream source to dynamically build the XML result through an empty XSL transformation. This way everything will be treated as a stream and your program will be able to handle very large documents without encountering performance issue. Also, you'll show your understanding of subjacent parsing SAX standards and streaming concepts.
Here is a great example of this kind of implementation: http://mobile.developer.com/xml/article.php/2108031/Transforming-Flat-Files-To-XML-With-SAX-and-XSLT.htm (with a "properties" file as source instead of a CSV).
If performances (or handling big documents) aren't an issue, a simple BufferedReader that directly parses the CSV file line by line and creates a Document in RAM using DocumentBuilder.newDocument() will be a simpler way to get the job done "the DOM way".
https://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilder.html
Here is a complete example :
SAXExample.java (the useful part of the code)
public class SAXExample {
public static void main(String[] args) {
try (InputStream is = SAXExample.class.getResourceAsStream("/demo.csv");
InputStream transformerInputStream = SAXExample.class
.getResourceAsStream("/echo.xsl")) {
XMLReader reader = new CSVFileParser();
reader.setContentHandler(new DefaultHandler());
TransformerFactory factory = TransformerFactory.newInstance();
SAXTransformerFactory saxTransFact = (SAXTransformerFactory) factory;
TransformerHandler transformerHandler = saxTransFact
.newTransformerHandler(
new StreamSource(transformerInputStream));
transformerHandler.setResult(new StreamResult(System.out));
reader.setContentHandler(transformerHandler);
reader.parse(new InputSource(is));
} catch (Exception e) {
e.printStackTrace();
}
}
public static class CSVFileParser extends AbstractXMLReader {
private static final Attributes EMPTY_ATTR = new AttributesImpl();
#Override
public void parse(InputSource input) throws IOException, SAXException {
BufferedReader reader = new BufferedReader(
new InputStreamReader(input.getByteStream()));
String buffer = null;
ContentHandler handler = getContentHandler();
if (handler == null)
return;
handler.startDocument();
while ((buffer = reader.readLine()) != null) {
String content = buffer.substring(buffer.indexOf(",") + 1,
buffer.lastIndexOf("\""));
if (buffer.startsWith("\"open,")) {
handler.startElement("", content, "", EMPTY_ATTR);
} else if (buffer.startsWith("\"close,")) {
handler.endElement("", "", content);
} else {
char[] chars = content.toCharArray();
handler.characters(chars, 0, chars.length);
}
}
handler.endDocument();
}
}
}
AbstractXMLReader.java (utility class to simply implements an XMLReader) :
public abstract class AbstractXMLReader implements org.xml.sax.XMLReader {
private Map<String, Object> featureMap = new HashMap<String, Object>();
private Map<String, Object> propertyMap = new HashMap<String, Object>();
private EntityResolver entityResolver;
private DTDHandler dtdHandler;
private ContentHandler contentHandler;
private ErrorHandler errorHandler;
public abstract void parse(InputSource input)
throws IOException, SAXException;
public boolean getFeature(String name)
throws SAXNotRecognizedException, SAXNotSupportedException {
Boolean featureValue = (Boolean) this.featureMap.get(name);
return (featureValue == null) ? false : featureValue.booleanValue();
}
public void setFeature(String name, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException {
this.featureMap.put(name, new Boolean(value));
}
public Object getProperty(String name)
throws SAXNotRecognizedException, SAXNotSupportedException {
return this.propertyMap.get(name);
}
public void setProperty(String name, Object value)
throws SAXNotRecognizedException, SAXNotSupportedException {
this.propertyMap.put(name, value);
}
public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}
public EntityResolver getEntityResolver() {
return this.entityResolver;
}
public void setDTDHandler(DTDHandler dtdHandler) {
this.dtdHandler = dtdHandler;
}
public DTDHandler getDTDHandler() {
return this.dtdHandler;
}
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
public ContentHandler getContentHandler() {
return this.contentHandler;
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
public ErrorHandler getErrorHandler() {
return this.errorHandler;
}
public void parse(String systemId) throws IOException, SAXException {
parse(new InputSource(systemId));
}
}
echo.xsl (the "empty" XSLT transformation to only output the original XML as generated by our custom parser)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes" />
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Using this class with your example output the following :
<?xml version="1.0" encoding="UTF-8"?><story>
<id>1234</id>
<snaps>
<snap/>
<snap/>
<snap/>
<snap/>
</snaps>
</story>
The CSV Parser is a "quick & dirty" implementation that does not really implements the full CSV specifications, but only your needs.
I think understanding these XML parsing and XSL transformation concepts were the goals of this exercise.
Related
I've been following the documentation for the webview2 on microsoft's official website but I have encountered a problem that I am not sure how to fix.
I have added a .NET object using AddHostObjectToScript and it works as long as the function has no parameter. When calling the object function that has a parameter in JS, I keep on getting a "parameter is incorrect" error.
This is how I am calling the host objects in angular app:
result = await window?.chrome?.webview?.hostObjects.bridge.Func("John");
and this is from my WinUI 3.0 app:
[ComVisible(true)]
public class Bridge
{
public string Func(string param)
{
return "Example: " + param;
}
public string Sample()
{
return "Example: ";
}
public BridgeAnotherClass AnotherObject { get; set; } = new BridgeAnotherClass();
// Sample indexed property.
[System.Runtime.CompilerServices.IndexerName("Items")]
public string this[int index]
{
get { return m_dictionary[index]; }
set { m_dictionary[index] = value; }
}
private Dictionary<int, string> m_dictionary = new Dictionary<int, string>();
}
public sealed partial class WebViewPage : Page
{
public WebViewViewModel ViewModel { get; }
public WebViewPage()
{
ViewModel = Ioc.Default.GetService<WebViewViewModel>();
InitializeComponent();
ViewModel.WebViewService.Initialize(webView);
webView.WebMessageReceived += getMsg;
InitializeAsync();
}
async void InitializeAsync()
{
await webView.EnsureCoreWebView2Async();
var interop = webView.CoreWebView2.As<ICoreWebView2Interop>();
interop.AddHostObjectToScript("bridge", new Bridge());
}
WebView2 currently has an issue where the WinRT API's interop interface AddHostObjectToScript doesn't work well with .NET objects. This is a bug in WebView2.
I want to implement a mechanism in a custom webview client (without JavaScript injection) that can block ads. Is a way I can catch ads and replace them with other ads from a trusted source?
Thanks
In your custom WebViewClient, you can override the function shouldInterceptRequest(WebView, WebResourceRequest).
From Android docs:
Notify the host application of a resource request and allow the application to return the data.
So the general idea is to check if the request is coming from an ad URL (plenty of black list filters out there), then return a "fake" resource that isn't the ad.
For a more in depth explanation plus an example, I recommend checking out this blog post.
To implement this, you have two options:
Use Javascript injected code to do this (which you explicitely said, don't want)
In WebView, instead of "http://example.com" load "http://myproxy.com?t=http://example.com" (properly escaped, of course) and setup "myproxy.com" to be a proxy which will fetch the upstream page (given in "t" query parameter, or in any other way) and replace ads with the trusted ones before sending response to the client. This will be pretty complex, though, because ads can be in many forms, they're usually Javascript injected themselves and you'd probably need to rewrite a lot of URL's in the fetched HTML, CSS and JS files etc.
I made a custom WebViewClient like:
public class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) { }
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".mp4")) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), "video/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
return true;
} else if (url.startsWith("tel:") || url.startsWith("sms:") || url.startsWith("smsto:")
|| url.startsWith("mms:") || url.startsWith("mmsto:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
private Map<String, Boolean> loadedUrls = new HashMap<>();
#SuppressWarnings("deprecation")
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
boolean ad;
if (!loadedUrls.containsKey(url)) {
ad = AdBlocker.isAd(url);
loadedUrls.put(url, ad);
} else {
ad = loadedUrls.get(url);
}
return ad ? AdBlocker.createEmptyResource() :
super.shouldInterceptRequest(view, url);
}
}
And created an AdBlocker class like:
public class AdBlocker {
private static final Set<String> AD_HOSTS = new HashSet<>();
public static boolean isAd(String url) {
try {
return isAdHost(getHost(url));
} catch (MalformedURLException e) {
Log.e("Devangi..", e.toString());
return false;
}
}
private static boolean isAdHost(String host) {
if (TextUtils.isEmpty(host)) {
return false;
}
int index = host.indexOf(".");
return index >= 0 && (AD_HOSTS.contains(host) ||
index + 1 < host.length() && isAdHost(host.substring(index + 1)));
}
public static WebResourceResponse createEmptyResource() {
return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes()));
}
public static String getHost(String url) throws MalformedURLException {
return new URL(url).getHost();
}
}
And use this WebViewClient in your oncreate like:
webview.setWebViewClient(new MyWebViewClient());
I’m trying to upload an excel in servlet and process it. While uploading I set enctype=“multipart/form-data” in my form. But in my servlet .isMultiPart(request) returns false.
JSP code:
function fSubir()
{
fFreezeButtons();
this.document.forms[0].action="../servlet/renault.saf.demandepiece.demandes.servlet.AjouterPoste";
if (this.document.forms[0].Flag.value == "1")
{
this.document.forms[0].Flag.value = "0";
this.document.forms[0].submit();
}
}
Select .xlsx type File :
<input type="submit" value="upload" onclick="fSubir()"/>
My .Jsp also has another form of get method which doesn’t have any enctype.
Servlet code;
public class AjouterPoste extends SapprServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
private final String UPLOAD_DIRECTORY = "/appli01/safdev01/saf_cl2/test/";
public void performTask(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
System.out.println("inside the AjouterPoste class - performTask");
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
System.out.println("Inside doPost:"+isMultipart+":"+request);
Please find the parts of my code on which I’m trying to upload a file.
When you submit a form having multipart/form-data, you can't use request.getParameter(paramName). Instead use the code below (part of the Apache FileUpload library)
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// this part is used instead of request.getParameter
String fieldName = item.getFieldName();
String fieldValue = item.getString();
// do something here
} else {
// this is the file processing part
String fieldName = item.getFieldName();
String fileName = FilenameUtils.getName(item.getName());
InputStream fileContent = item.getInputStream();
...
}
}
} catch (FileUploadException e) {
throw new ServletException("exception", e);
}
You can tell that a specific item is a regular form item (and not a file) by checking that FileItem.isFormField() method returns true.
Can this be done ?
Below are snippets of what is not working, failing with exception:
function SendBytesJS() {
var control1 = document.getElementById('sl1');
bytes = new Array(1, 2, 3);
control1.Content.MainPage.SendBytesSL(bytes);
}
and
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
System.Windows.Browser.HtmlPage.RegisterScriptableObject("MainPage", this);
}
[System.Windows.Browser.ScriptableMember]
public void SendBytesSL(byte[] bytes)
{
// never gets here
}
}
The HTML bridge does not support byte arrays and Javascript only understands integer and float (actually double).
An array is passed as object[] and numbers are always passed as double. Hence your code needs to look more like the following:-
// Warning untested code
[ScriptableMember]
public void SendBytesSL(object[] arrayIn)
{
byte[] bytes = arrayIn.Select(o => Convert.ToByte(o)).ToArray();
}
I am trying to call some javascript functions sitting in an html page running inside an android webview. Pretty simple what the code tries to do below - from the android app, call a javascript function with a test message, which inturn calls a java function back in the android app that displays test message via toast.
The javascript function looks like:
function testEcho(message){
window.JSInterface.doEchoTest(message);
}
From the WebView, I have tried calling the javascript the following ways with no luck:
myWebView.loadUrl("javascript:testEcho(Hello World!)");
mWebView.loadUrl("javascript:(function () { " + "testEcho(Hello World!);" + "})()");
I did enable javascript on the WebView
myWebView.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
myWebView.addJavascriptInterface(myJSInterface, "JSInterface");
And heres the Java Class
public class JSInterface{
private WebView mAppView;
public JSInterface (WebView appView) {
this.mAppView = appView;
}
public void doEchoTest(String echo){
Toast toast = Toast.makeText(mAppView.getContext(), echo, Toast.LENGTH_SHORT);
toast.show();
}
}
I've spent a lot of time googling around to see what I may be doing wrong. All examples I have found use this approach. Does anyone see something wrong here?
Edit: There are several other external javascript files being referenced & used in the html, could they be the issue?
I figured out what the issue was : missing quotes in the testEcho() parameter. This is how I got the call to work:
myWebView.loadUrl("javascript:testEcho('Hello World!')");
From kitkat onwards use evaluateJavascript method instead loadUrl to call the javascript functions like below
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("enable();", null);
} else {
webView.loadUrl("javascript:enable();");
}
public void run(final String scriptSrc) {
webView.post(new Runnable() {
#Override
public void run() {
webView.loadUrl("javascript:" + scriptSrc);
}
});
}
I created a nice wrapper to call JavaScript methods; it also shows JavaScript errors in log:
private void callJavaScript(String methodName, Object...params){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("javascript:try{");
stringBuilder.append(methodName);
stringBuilder.append("(");
for (int i = 0; i < params.length; i++) {
Object param = params[i];
if(param instanceof String){
stringBuilder.append("'");
stringBuilder.append(param.toString().replace("'", "\\'"));
stringBuilder.append("'");
}
if(i < params.length - 1){
stringBuilder.append(",");
}
}
stringBuilder.append(")}catch(error){Android.onError(error.message);}");
webView.loadUrl(stringBuilder.toString());
}
You need to add this too:
private class WebViewInterface{
#JavascriptInterface
public void onError(String error){
throw new Error(error);
}
}
And add this interface to your webview:
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");
Yes you have the syntax error. If you want to get your Javascript errors and printing statements in your logcat you must implement the onConsoleMessage(ConsoleMessage cm) method in your WebChromeClient. It gives the complete stack traces like Web console(Inspect element). Here is the method.
public boolean onConsoleMessage(ConsoleMessage cm)
{
Log.d("Message", cm.message() + " -- From line "
+ cm.lineNumber() + " of "
+ cm.sourceId() );
return true;
}
After implementation you will get your Javascript errors and print statements (console.log) on your logcat.
Modification of #Ilya_Gazman answer
private void callJavaScript(WebView view, String methodName, Object...params){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("javascript:try{");
stringBuilder.append(methodName);
stringBuilder.append("(");
String separator = "";
for (Object param : params) {
stringBuilder.append(separator);
separator = ",";
if(param instanceof String){
stringBuilder.append("'");
}
stringBuilder.append(param.toString().replace("'", "\\'"));
if(param instanceof String){
stringBuilder.append("'");
}
}
stringBuilder.append(")}catch(error){console.error(error.message);}");
final String call = stringBuilder.toString();
Log.i(TAG, "callJavaScript: call="+call);
view.loadUrl(call);
}
will correctly create JS calls e.g.
callJavaScript(mBrowser, "alert", "abc", "def");
//javascript:try{alert('abc','def')}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:try{alert(1,true,'abc')}catch(error){console.error(error.message);}
Note that objects will not be passed correctly - but you can serialize them before passing as an argument.
Also I've changed where the error goes, I've diverted it to the console log which can be listened by:
webView.setWebChromeClient(new CustomWebChromeClient());
and client
class CustomWebChromeClient extends WebChromeClient {
private static final String TAG = "CustomWebChromeClient";
#Override
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.d(TAG, String.format("%s # %d: %s", cm.message(),
cm.lineNumber(), cm.sourceId()));
return true;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.bluapp.androidview.R;
public class WebViewActivity3 extends AppCompatActivity {
private WebView webView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view3);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("file:///android_asset/webview1.html");
webView.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView view, String weburl){
webView.loadUrl("javascript:testEcho('Javascript function in webview')");
}
});
}
}
assets file
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>WebView1</title>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
</head>
<body style="background-color:#212121">
<script type="text/javascript">
function testEcho(p1){
document.write(p1);
}
</script>
</body>
</html>
Here is an example to load js script from the asset on WebView.
Put script to a file will help reading easier
I load the script in onPageFinished because I need to access some DOM element inside the script (to able to access it should be loaded or it will be null). Depend on the purpose of the script, we may load it earlier
assets/myjsfile.js
document.getElementById("abc").innerText = "def"
document.getElementById("abc").onclick = function() {
document.getElementById("abc").innerText = "abc"
}
WebViewActivity
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
val script = readTextFromAsset("myjsfile.js")
view.loadUrl("javascript: $script")
}
}
fun readTextFromAsset(context: Context, fileName: String): String {
return context.assets.open(fileName).bufferedReader().use { it.readText()
}