Ballerina : How to send attachment with email - http

I am using wso2/gmail package to send an email notification. According to the documentation (https://central.ballerina.io/wso2/gmail) we can send an attachment with the mail through the package. However, when I try to define attachment paths as a parameter, I get an error as follows.
incompatible types: expected 'wso2/gmail:0.9.7:AttachmentPath', found 'string'
What is AttachmentPath type? Can we parse string array of attachment paths to AttachmentPath? Here is my function to send mail.
import wso2/gmail;
import ballerina/io;
import ballerina/log;
import ballerina/config;
import ballerina/http;
function sendErrorLogMail(string senderEmail, string recipientEmail, string subject, string messageBody) returns boolean {
endpoint gmail:Client gmailErrorClient {
clientConfig:{
auth:{
accessToken:config:getAsString("gmailApiConfiguration.accessToken"),
refreshToken:config:getAsString("gmailApiConfiguration.refreshToken"),
clientId:config:getAsString("gmailApiConfiguration.clientId"),
clientSecret:config:getAsString("gmailApiConfiguration.clientSecret")
}
}
};
gmail:MessageRequest messageRequest;
messageRequest.recipient = recipientEmail;
messageRequest.sender = senderEmail;
messageRequest.subject = subject;
messageRequest.messageBody = messageBody;
messageRequest.contentType = gmail:TEXT_HTML;
//What is the attachment path?
AttachmentPath attachmentPath = "./org/wso2/logs/loginfo.txt";
messageRequest.attachmentPaths = attachmentPath;
var sendMessageResponse = gmailErrorClient->sendMessage(senderEmail, untaint messageRequest);
string messageId;
string threadId;
match sendMessageResponse {
(string, string) sendStatus => {
(messageId, threadId) = sendStatus;
log:printInfo("Sent email to " + recipientEmail + " with message Id: " + messageId + " and thread Id:"
+ threadId);
return true;
}
gmail:GmailError e => {
log:printInfo(e.message);
return false;
}
}
}

Yes. As #pasan has mentioned, AttachmentPath is a record. Following is the updated code if someone wants to refer.
function sendErrorLogMail(string senderEmail, string recipientEmail, string subject, string messageBody) returns boolean {
endpoint gmail:Client gmailErrorClient {
clientConfig:{
auth:{
accessToken:config:getAsString("gmailApiConfiguration.accessToken"),
refreshToken:config:getAsString("gmailApiConfiguration.refreshToken"),
clientId:config:getAsString("gmailApiConfiguration.clientId"),
clientSecret:config:getAsString("gmailApiConfiguration.clientSecret")
}
}
};
gmail:AttachmentPath attachmentPath= {
attachmentPath:"./org/wso2/logs/loginfo.txt",
mimeType:"Text/plain"
};
gmail:MessageRequest messageRequest;
messageRequest.recipient = recipientEmail;
messageRequest.sender = senderEmail;
messageRequest.subject = subject;
messageRequest.messageBody = messageBody;
messageRequest.contentType = gmail:TEXT_HTML;
messageRequest.attachmentPaths = [attachmentPath];
var sendMessageResponse = gmailErrorClient->sendMessage(senderEmail, untaint messageRequest);
string messageId;
string threadId;
match sendMessageResponse {
(string, string) sendStatus => {
(messageId, threadId) = sendStatus;
log:printInfo("Sent email to " + recipientEmail + " with message Id: " + messageId + " and thread Id:"
+ threadId);
return true;
}
gmail:GmailError e => {
log:printInfo(e.message);
return false;
}
}
}

AttachmentPath is defined in [wso2/gmail][1] as a record. The attachmentPaths field needs an array of such AttachmentPath objects. So following should work.
gmail:AttachmentPath attachmentPath= {
attachmentPath:"./org/wso2/logs/loginfo.txt",
mimeType:"text/plain"
};
messageRequest.attachmentPaths = [attachmentPaths];

Related

How to send a zipped file to S3 bucket from Apex?

Folks,
I am trying to move data to s3 from Salesforce using apex class. I have been told by the data manager to send the data in zip/gzip format to the S3 bucket for storage cost savings.
I have simply tried to do a request.setCompressed(true); as I've read it compresses the body before sending it to the endpoint. Code below:
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:'+DATA_NAMED_CRED+'/'+URL+'/'+generateUniqueTimeStampforSuffix());
request.setMethod('PUT');
request.setBody(JSON.serialize(data));
request.setCompressed(true);
request.setHeader('Content-Type','application/json');
But no matter what I always receive this:
<Error><Code>XAmzContentSHA256Mismatch</Code><Message>The provided 'x-amz-content-sha256' header does not match what was computed.</Message><ClientComputedContentSHA256>fd31b2b9115ef77e8076b896cb336d21d8f66947210ffcc9c4d1971b2be3bbbc</ClientComputedContentSHA256><S3ComputedContentSHA256>1e7f2115e60132afed9e61132aa41c3224c6e305ad9f820e6893364d7257ab8d</S3ComputedContentSHA256>
I have tried multiple headers too, like setting the content type to gzip/zip, etc.
Any pointers in the right direction would be appreciated.
I had a good amount of headaches attempting to do a similar thing. I feel your pain.
The following code has worked for us using lambda functions; you can try modifying it and see what happens.
public class AwsApiGateway {
// Things we need to know about the service. Set these values in init()
String host, payloadSha256;
String resource;
String service = 'execute-api';
String region;
public Url endpoint;
String accessKey;
String stage;
string secretKey;
HttpMethod method = HttpMethod.XGET;
// Remember to set "payload" here if you need to specify a body
// payload = Blob.valueOf('some-text-i-want-to-send');
// This method helps prevent leaking secret key,
// as it is never serialized
// Url endpoint;
// HttpMethod method;
Blob payload;
// Not used externally, so we hide these values
Blob signingKey;
DateTime requestTime;
Map<String, String> queryParams = new map<string,string>(), headerParams = new map<string,string>();
void init(){
if (payload == null) payload = Blob.valueOf('');
requestTime = DateTime.now();
createSigningKey(secretKey);
}
public AwsApiGateway(String resource){
this.stage = AWS_LAMBDA_STAGE
this.resource = '/' + stage + '/' + resource;
this.region = AWS_REGION;
this.endpoint = new Url(AWS_ENDPOINT);
this.accessKey = AWS_ACCESS_KEY;
this.secretKey = AWS_SECRET_KEY;
}
// Make sure we can't misspell methods
public enum HttpMethod { XGET, XPUT, XHEAD, XOPTIONS, XDELETE, XPOST }
public void setMethod (HttpMethod method){
this.method = method;
}
public void setPayload (string payload){
this.payload = Blob.valueOf(payload);
}
// Add a header
public void setHeader(String key, String value) {
headerParams.put(key.toLowerCase(), value);
}
// Add a query param
public void setQueryParam(String key, String value) {
queryParams.put(key.toLowerCase(), uriEncode(value));
}
// Create a canonical query string (used during signing)
String createCanonicalQueryString() {
String[] results = new String[0], keys = new List<String>(queryParams.keySet());
keys.sort();
for(String key: keys) {
results.add(key+'='+queryParams.get(key));
}
return String.join(results, '&');
}
// Create the canonical headers (used for signing)
String createCanonicalHeaders(String[] keys) {
keys.addAll(headerParams.keySet());
keys.sort();
String[] results = new String[0];
for(String key: keys) {
results.add(key+':'+headerParams.get(key));
}
return String.join(results, '\n')+'\n';
}
// Create the entire canonical request
String createCanonicalRequest(String[] headerKeys) {
return String.join(
new String[] {
method.name().removeStart('X'), // METHOD
new Url(endPoint, resource).getPath(), // RESOURCE
createCanonicalQueryString(), // CANONICAL QUERY STRING
createCanonicalHeaders(headerKeys), // CANONICAL HEADERS
String.join(headerKeys, ';'), // SIGNED HEADERS
payloadSha256 // SHA256 PAYLOAD
},
'\n'
);
}
// We have to replace ~ and " " correctly, or we'll break AWS on those two characters
string uriEncode(String value) {
return value==null? null: EncodingUtil.urlEncode(value, 'utf-8').replaceAll('%7E','~').replaceAll('\\+','%20');
}
// Create the entire string to sign
String createStringToSign(String[] signedHeaders) {
String result = createCanonicalRequest(signedHeaders);
return String.join(
new String[] {
'AWS4-HMAC-SHA256',
headerParams.get('date'),
String.join(new String[] { requestTime.formatGMT('yyyyMMdd'), region, service, 'aws4_request' },'/'),
EncodingUtil.convertToHex(Crypto.generateDigest('sha256', Blob.valueof(result)))
},
'\n'
);
}
// Create our signing key
void createSigningKey(String secretKey) {
signingKey = Crypto.generateMac('hmacSHA256', Blob.valueOf('aws4_request'),
Crypto.generateMac('hmacSHA256', Blob.valueOf(service),
Crypto.generateMac('hmacSHA256', Blob.valueOf(region),
Crypto.generateMac('hmacSHA256', Blob.valueOf(requestTime.formatGMT('yyyyMMdd')), Blob.valueOf('AWS4'+secretKey))
)
)
);
}
// Create all of the bits and pieces using all utility functions above
public HttpRequest createRequest() {
init();
payloadSha256 = EncodingUtil.convertToHex(Crypto.generateDigest('sha-256', payload));
setHeader('date', requestTime.formatGMT('yyyyMMdd\'T\'HHmmss\'Z\''));
if(host == null) {
host = endpoint.getHost();
}
setHeader('host', host);
HttpRequest request = new HttpRequest();
request.setMethod(method.name().removeStart('X'));
if(payload.size() > 0) {
setHeader('Content-Length', String.valueOf(payload.size()));
request.setBodyAsBlob(payload);
}
String finalEndpoint = new Url(endpoint, resource).toExternalForm(),
queryString = createCanonicalQueryString();
if(queryString != '') {
finalEndpoint += '?'+queryString;
}
request.setEndpoint(finalEndpoint);
for(String key: headerParams.keySet()) {
request.setHeader(key, headerParams.get(key));
}
String[] headerKeys = new String[0];
String stringToSign = createStringToSign(headerKeys);
request.setHeader(
'Authorization',
String.format(
'AWS4-HMAC-SHA256 Credential={0}, SignedHeaders={1},Signature={2}',
new String[] {
String.join(new String[] { accessKey, requestTime.formatGMT('yyyyMMdd'), region, service, 'aws4_request' },'/'),
String.join(headerKeys,';'), EncodingUtil.convertToHex(Crypto.generateMac('hmacSHA256', Blob.valueOf(stringToSign), signingKey))}
));
system.debug(json.serializePretty(request.getEndpoint()));
return request;
}
// Actually perform the request, and throw exception if response code is not valid
public HttpResponse sendRequest(Set<Integer> validCodes) {
HttpResponse response = new Http().send(createRequest());
if(!validCodes.contains(response.getStatusCode())) {
system.debug(json.deserializeUntyped(response.getBody()));
}
return response;
}
// Same as above, but assume that only 200 is valid
// This method exists because most of the time, 200 is what we expect
public HttpResponse sendRequest() {
return sendRequest(new Set<Integer> { 200 });
}
// TEST METHODS
public static string getEndpoint(string attribute){
AwsApiGateway api = new AwsApiGateway(attribute);
return api.createRequest().getEndpoint();
}
public static string getEndpoint(string attribute, map<string, string> params){
AwsApiGateway api = new AwsApiGateway(attribute);
for (string key: params.keySet()){
api.setQueryParam(key, params.get(key));
}
return api.createRequest().getEndpoint();
}
public class EndpointConfig {
string resource;
string attribute;
list<object> items;
map<string,string> params;
public EndpointConfig(string resource, string attribute, list<object> items){
this.items = items;
this.resource = resource;
this.attribute = attribute;
}
public EndpointConfig setQueryParams(map<string,string> parameters){
params = parameters;
return this;
}
public string endpoint(){
if (params == null){
return getEndpoint(resource);
} else return getEndpoint(resource + '/' + attribute, params);
}
public SingleRequestMock mockResponse(){
return new SingleRequestMock(200, 'OK', json.serialize(items), null);
}
}
}

How to use Generic Repository for SOAP in Xamarin Form

I used Generic Repository in my sample project in Xamrin form to get data from api.
so my question is how can I make the following code Repository for soap webervice to become generic. so that the If you know of an example or a blog post please point me to the right direction
it is my Sample code:
public interface IGenericRepository
{
Task<T> GetAsync<T>(string uri, string authToken = "");
}
and impeliment interface:
public class GenericRepository: IGenericRepository
{
public async Task<T> GetAsync<T>(string uri, string authToken = "")
{
try
{
HttpClient httpClient = CreateHttpClient(uri);
string jsonResult = string.Empty;
var responseMessage = await Policy
.Handle<WebException>(ex =>
{
Debug.WriteLine($"{ex.GetType().Name + " : " + ex.Message}");
return true;
})
.WaitAndRetryAsync
(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
)
.ExecuteAsync(async () => await httpClient.GetAsync(uri));
if (responseMessage.IsSuccessStatusCode)
{
jsonResult =
await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
var json = JsonConvert.DeserializeObject<T>(jsonResult);
return json;
}
if (responseMessage.StatusCode == HttpStatusCode.Forbidden ||
responseMessage.StatusCode == HttpStatusCode.Unauthorized)
{
throw new ServiceAuthenticationException(jsonResult);
}
throw new HttpRequestExceptionEx(responseMessage.StatusCode, jsonResult);
}
catch (Exception e)
{
Debug.WriteLine($"{ e.GetType().Name + " : " + e.Message}");
throw;
}
}
}

How to make Digest Authentication with http in Flutter?

I am trying to make an ApI request with Digest Authentication. I found an answer to the above question FLUTTER How to implement Digest Authentification but it is not very clear. The docs for digest are very minimal.
Following is my code
import 'package:http/io_client.dart' as io_client;
import 'package:http/http.dart' as http;
try {
HttpClient authenticatingClient = HttpClient();
authenticatingClient.authenticate = (uri, scheme, realm) {
authenticatingClient.addCredentials(
uri,
realm,
HttpClientDigestCredentials(
DIGEST_AUTH_USERNAME, DIGEST_AUTH_PASSWORD));
return Future.value(true);
};
http.Client client = io_client.IOClient(authenticatingClient);
final response = await client.post(LOGIN_URL, body: {
"username": userName,
"password": password,
"user_group": 2
}).timeout(const Duration(seconds: 20));
if (response.statusCode == 200) {
debugPrint(response.body);
CurvesLoginModel curvesLoginModel = standardSerializers.deserializeWith(
CurvesLoginModel.serializer, json.decode(response.body));
return curvesLoginModel;
} else {
return null;
}
} on TimeoutException catch (_) {
return null;
} on SocketException catch (_) {
return null;
}
}
But what is realm in addCredentials.
Also is this the way to implement Digest Authentication in http for Flutter?
As soon as I hit my endpoint I get the following error Unhandled Exception: type 'int' is not a subtype of type 'String' in type cast
Realm is an arbitrary string provided by the web server to help you decide which username to use, in case you have more than one. It's sort of analogous to domain. In one domain your username might be fbloggs, in another fredb. By telling you the realm/domain you know which to provide.
Your cast problem is caused by using the value 2 in the body. That must be a Map<String, String>, but you have provided an integer. Replace it with 2.toString().
If somebody wants to know how to make digest auth with http then it is as follows
import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:http/http.dart' as http;
class DigestAuthClient extends http.BaseClient {
DigestAuthClient(String username, String password, {inner})
: _auth = DigestAuth(username, password),
// ignore: prefer_if_null_operators
_inner = inner == null ? http.Client() : inner;
final http.Client _inner;
final DigestAuth _auth;
void _setAuthString(http.BaseRequest request) {
request.headers['Authorization'] =
_auth.getAuthString(request.method, request.url);
}
#override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final response = await _inner.send(request);
if (response.statusCode == 401) {
final newRequest = copyRequest(request);
final String authInfo = response.headers['www-authenticate'];
_auth.initFromAuthorizationHeader(authInfo);
_setAuthString(newRequest);
return _inner.send(newRequest);
}
// we should reach this point only with errors other than 401
return response;
}
}
Map<String, String> splitAuthenticateHeader(String header) {
if (header == null || !header.startsWith('Digest ')) {
return null;
}
String token = header.substring(7); // remove 'Digest '
var ret = <String, String>{};
final components = token.split(',').map((token) => token.trim());
for (final component in components) {
final kv = component.split('=');
ret[kv[0]] = kv.getRange(1, kv.length).join('=').replaceAll('"', '');
}
return ret;
}
String md5Hash(String data) {
var content = const Utf8Encoder().convert(data);
var md5 = crypto.md5;
var digest = md5.convert(content).toString();
return digest;
}
// from http_retry
/// Returns a copy of [original].
http.Request _copyNormalRequest(http.Request original) {
var request = http.Request(original.method, original.url)
..followRedirects = original.followRedirects
..persistentConnection = original.persistentConnection
..body = original.body;
request.headers.addAll(original.headers);
request.maxRedirects = original.maxRedirects;
return request;
}
http.BaseRequest copyRequest(http.BaseRequest original) {
if (original is http.Request) {
return _copyNormalRequest(original);
} else {
throw UnimplementedError(
'cannot handle yet requests of type ${original.runtimeType}');
}
}
// Digest auth
String _formatNonceCount(int nc) {
return nc.toRadixString(16).padLeft(8, '0');
}
String _computeHA1(String realm, String algorithm, String username,
String password, String nonce, String cnonce) {
String ha1;
if (algorithm == null || algorithm == 'MD5') {
final token1 = "$username:$realm:$password";
ha1 = md5Hash(token1);
} else if (algorithm == 'MD5-sess') {
final token1 = "$username:$realm:$password";
final md51 = md5Hash(token1);
final token2 = "$md51:$nonce:$cnonce";
ha1 = md5Hash(token2);
}
return ha1;
}
Map<String, String> computeResponse(
String method,
String path,
String body,
String algorithm,
String qop,
String opaque,
String realm,
String cnonce,
String nonce,
int nc,
String username,
String password) {
var ret = <String, String>{};
// ignore: non_constant_identifier_names
String HA1 = _computeHA1(realm, algorithm, username, password, nonce, cnonce);
// ignore: non_constant_identifier_names
String HA2;
if (qop == 'auth-int') {
final bodyHash = md5Hash(body);
final token2 = "$method:$path:$bodyHash";
HA2 = md5Hash(token2);
} else {
// qop in [null, auth]
final token2 = "$method:$path";
HA2 = md5Hash(token2);
}
final nonceCount = _formatNonceCount(nc);
ret['username'] = username;
ret['realm'] = realm;
ret['nonce'] = nonce;
ret['uri'] = path;
ret['qop'] = qop;
ret['nc'] = nonceCount;
ret['cnonce'] = cnonce;
if (opaque != null) {
ret['opaque'] = opaque;
}
ret['algorithm'] = algorithm;
if (qop == null) {
final token3 = "$HA1:$nonce:$HA2";
ret['response'] = md5Hash(token3);
} else if (qop == 'auth' || qop == 'auth-int') {
final token3 = "$HA1:$nonce:$nonceCount:$cnonce:$qop:$HA2";
ret['response'] = md5Hash(token3);
}
return ret;
}
class DigestAuth {
DigestAuth(this.username, this.password);
String username;
String password;
// must get from first response
String _algorithm;
String _qop;
String _realm;
String _nonce;
String _opaque;
int _nc = 0; // request counter
String _cnonce; // client-generated; should change for each request
String _computeNonce() {
math.Random rnd = math.Random();
List<int> values = List<int>.generate(16, (i) => rnd.nextInt(256));
return hex.encode(values);
}
String getAuthString(String method, Uri url) {
_cnonce = _computeNonce();
_nc += 1;
// if url has query parameters, append query to path
var path = url.hasQuery ? "${url.path}?${url.query}" : url.path;
// after the first request we have the nonce, so we can provide credentials
var authValues = computeResponse(method, path, '', _algorithm, _qop,
_opaque, _realm, _cnonce, _nonce, _nc, username, password);
final authValuesString = authValues.entries
.where((e) => e.value != null)
.map((e) => [e.key, '="', e.value, '"'].join(''))
.toList()
.join(', ');
final authString = 'Digest $authValuesString';
return authString;
}
void initFromAuthorizationHeader(String authInfo) {
Map<String, String> values = splitAuthenticateHeader(authInfo);
_algorithm = values['algorithm'];
_qop = values['qop'];
_realm = values['realm'];
_nonce = values['nonce'];
_opaque = values['opaque'];
}
bool isReady() {
return _nonce != null;
}
}
Then when calling your api
final response =
await DigestAuthClient(DIGEST_AUTH_USERNAME, DIGEST_AUTH_PASSWORD)
.post(LOGIN_URL, body: {
"USERNAME": userName,
"PASSWORD": password,
"USER_GROUP": "2"
}).timeout(const Duration(seconds: 20));
All credit goes to the following library https://pub.dev/packages/http_auth

Wikipedia page parsing program caught in endless graph cycle

My program is caught in a cycle that never ends, and I can't see how it get into this trap, or how to avoid it.
It's parsing Wikipedia data and I think it's just following a connected component around and around.
Maybe I can store the pages I've visited already in a set and if a page is in that set I won't go back to it?
This is my project, its quite small, only three short classes.
This is a link to the data it generates, I stopped it short, otherwise it would have gone on and on.
This is the laughably small toy input that generated that mess.
It's the same project I was working on when I asked this question.
What follows is the entirety of the code.
The main class:
public static void main(String[] args) throws Exception
{
String name_list_file = "/home/matthias/Workbench/SUTD/nytimes_corpus/NYTimesCorpus/2005/01/02/test/people_test.txt";
String single_name;
try (
// read in the original file, list of names, w/e
InputStream stream_for_name_list_file = new FileInputStream( name_list_file );
InputStreamReader stream_reader = new InputStreamReader( stream_for_name_list_file , Charset.forName("UTF-8"));
BufferedReader line_reader = new BufferedReader( stream_reader );
)
{
while (( single_name = line_reader.readLine() ) != null)
{
//replace this by a URL encoder
//String associated_alias = single_name.replace(' ', '+');
String associated_alias = URLEncoder.encode( single_name , "UTF-8");
String platonic_key = single_name;
System.out.println("now processing: " + platonic_key);
Wikidata_Q_Reader.getQ( platonic_key, associated_alias );
}
}
//print the struc
Wikidata_Q_Reader.print_data();
}
The Wikipedia reader / value grabber:
static Map<String, HashSet<String> > q_valMap = new HashMap<String, HashSet<String> >();
//public static String[] getQ(String variable_entity) throws Exception
public static void getQ( String platonic_key, String associated_alias ) throws Exception
{
//get the corresponding wikidata page
//check the validity of the URL
String URL_czech = "https://www.wikidata.org/wiki/Special:ItemByTitle?site=en&page=" + associated_alias + "&submit=Search";
URL wikidata_page = new URL(URL_czech);
HttpURLConnection wiki_connection = (HttpURLConnection)wikidata_page.openConnection();
InputStream wikiInputStream = null;
try
{
// try to connect and use the input stream
wiki_connection.connect();
wikiInputStream = wiki_connection.getInputStream();
}
catch(IOException e)
{
// failed, try using the error stream
wikiInputStream = wiki_connection.getErrorStream();
}
BufferedReader wiki_data_pagecontent = new BufferedReader(
new InputStreamReader(
wikiInputStream ));
String line_by_line;
while ((line_by_line = wiki_data_pagecontent.readLine()) != null)
{
// if we can determine it's a disambig page we need to send it off to get all
// the possible senses in which it can be used.
Pattern disambig_pattern = Pattern.compile("<div class=\"wikibase-entitytermsview-heading-description \">Wikipedia disambiguation page</div>");
Matcher disambig_indicator = disambig_pattern.matcher(line_by_line);
if (disambig_indicator.matches())
{
//off to get the different usages
Wikipedia_Disambig_Fetcher.all_possibilities( platonic_key, associated_alias );
}
else
{
//get the Q value off the page by matching
Pattern q_page_pattern = Pattern.compile("<!-- wikibase-toolbar --><span class=\"wikibase-toolbar-container\"><span class=\"wikibase-toolbar-item " +
"wikibase-toolbar \">\\[<span class=\"wikibase-toolbar-item wikibase-toolbar-button wikibase-toolbar-button-edit\"><a " +
"href=\"/wiki/Special:SetSiteLink/(.*?)\">edit</a></span>\\]</span></span>");
Matcher match_Q_component = q_page_pattern.matcher(line_by_line);
if ( match_Q_component.matches() )
{
String Q = match_Q_component.group(1);
// 'Q' should be appended to an array, since each entity can hold multiple
// Q values on that basis of disambig
put_to_hash( platonic_key, Q );
}
}
}
wiki_data_pagecontent.close();
// \\ // ! PRINT IT ! // \\ // \\ // \\ // \\ // \\ // \\
for (Map.Entry<String, HashSet<String> > entry : q_valMap.entrySet())
{
System.out.println(entry.getKey()+" : " + Arrays.deepToString(q_valMap.entrySet().toArray()) );
}
}
// add Q values to their arrayList in the hash map at the index of the appropriate entity
public static HashSet<String> put_to_hash(String key, String value )
{
HashSet<String> valSet;
if (q_valMap.containsKey(key)) {
valSet = q_valMap.get(key);
} else {
valSet = new HashSet<String>();
q_valMap.put(key, valSet);
}
valSet.add(value);
return valSet;
}
// add Q values to their arrayList in the hash map at the index of the appropriate entity
public static void print_data()
{
System.out.println("THIS IS THE FINAL DATA SET!!!");
// \\ // ! PRINT IT ! // \\ // \\ // \\ // \\ // \\ // \\
for (Map.Entry<String, HashSet<String> > entry : q_valMap.entrySet())
{
System.out.println(entry.getKey()+" : " + Arrays.deepToString(q_valMap.entrySet().toArray()) );
}
}
Dealing with disambiguation pages:
public static void all_possibilities( String platonic_key, String associated_alias ) throws Exception
{
System.out.println("this is a disambig page");
//if it's a disambig page we know we can go right to the Wikipedia
//get it's normal wiki disambig page
String URL_czech = "https://en.wikipedia.org/wiki/" + associated_alias;
URL wikidata_page = new URL(URL_czech);
HttpURLConnection wiki_connection = (HttpURLConnection)wikidata_page.openConnection();
InputStream wikiInputStream = null;
try
{
// try to connect and use the input stream
wiki_connection.connect();
wikiInputStream = wiki_connection.getInputStream();
}
catch(IOException e)
{
// failed, try using the error stream
wikiInputStream = wiki_connection.getErrorStream();
}
// parse the input stream using Jsoup
Document docx = Jsoup.parse(wikiInputStream, null, wikidata_page.getProtocol()+"://"+wikidata_page.getHost()+"/");
//this can handle the less structured ones.
Elements linx = docx.select( "p:contains(" + associated_alias + ") ~ ul a:eq(0)" );
for (Element linq : linx)
{
System.out.println(linq.text());
String linq_nospace = URLEncoder.encode( linq.text() , "UTF-8");
Wikidata_Q_Reader.getQ( platonic_key, linq_nospace );
}
}

Invoke static method using Introspection in Flex

All,
While this is similar to another post, that post (does not indicate how to perform this (if it can be done) without instantiating an object. Also, without success I have tried multiple variations on the theme of:
class[method](arg)
class[method].call(arg)
method.apply(class, arg)
I am new to Flex, but have used Reflection in both C# and Java. BTW, the code that I am attempting to get to work in Flex is mirrored in both those languages and works as expected.
Any help is appreciated,
Thanks,
Todd
Non-functioning Flex Code:
private function ipMethodTester( ipMethodName:String,
shouldPass:Array, shouldFail:Array):void
{
var message:String;
var entry:String;
for each(entry in shouldPass)
{
message = ipMethodName + ": " + entry + " should pass";
try
{
Assert.assertTrue(message,
FieldValidator[ipMethodName](entry));
}
catch(e:Error)
{
Assert.fail(e.message + " " + message);
}
}
for each(entry in shouldFail)
{
message = ipMethodName + ": " + entry + " should fail";
try
{
Assert.assertFalse(message,
FieldValidator[ipMethodName](entry));
}
catch(e:Error)
{
Assert.fail(e.message + " " + message);
}
}
}
Java Code:
private void ipMethodTester(final String ipMethodName,
final String[] shouldPass, final String[] shouldFail)
{
Method method;
try
{
method = InetUtil.class.getDeclaredMethod(ipMethodName, String.class);
method.setAccessible(true);
for(String entry : shouldPass)
{
Object[] invokeArgs = { entry };
boolean passed = (Boolean)method.invoke(null, invokeArgs);
assertTrue(ipMethodName + ": " + entry + " should pass", passed);
}
for(String entry : shouldFail)
{
Object[] invokeArgs = { entry };
boolean passed = (Boolean)method.invoke(null, invokeArgs);
assertFalse(ipMethodName + ": " + entry + " should fail", passed);
}
}
catch (final Exception e)
{
fail(e.getClass().toString());
}
}
C# code:
private void ipMethodTester(string ipMethodName, string[] shouldPass, string[] shouldFail)
{
Type type = typeof (ValidateUtil);
BindingFlags bindingFlags = BindingFlags.InvokeMethod
| BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
MethodInfo method = type.GetMethod(ipMethodName, bindingFlags);
foreach(string entry in shouldPass)
{
object[] invokeArgs = { entry };
bool passed = (bool)method.Invoke(null, invokeArgs);
Assert.IsTrue(passed, ipMethodName + ": " + entry + " should pass");
}
foreach(string entry in shouldFail)
{
object[] invokeArgs = { entry };
bool passed = (bool)method.Invoke(null, invokeArgs);
Assert.IsFalse(passed, ipMethodName + ": " + entry + " should fail");
}
}
This works for me:
MyClass['myMethod']('arg1','arg2');
This also works:
MyClass['myMethod'].call(MyClass, 'arg1', 'arg2');
Note: the first argument of the 'call' method (MyClass in this case) just specifies which object is referenced when you use the 'this' keyword inside the function.

Resources