paging in Java for a lookup - java

I am trying to implement paging in an already running application, it works like mongodb where you get a query with bunch of parameters like
searchConditions ,limit( how many docs you want), skip ( skip these many docs)
and so on
and the response is back in json my task is to generate the previous and next link to the query that means previous page and the next page as per query
I came up with a method below
public Paging generateLink(RequestFilter filter) {
int prev_skip;
int prev_limit;
String pagePrev = "";
String pageNext = "";
int next_skip;
int next_limit;
String searchCondition = JsonUtils.toSimpleJsonString(filter.getSearch());
String url = "http://isgswmdi1n1.nam.nsroot.net:7014/account-search/rest/api/v1/pmm";
//properties.getProperty("paging.link");
System.out.println(url);
Paging pagingOption = new Paging();
Result result = new Result();
int count = luceneSearchService.searchCount(filter);
try {
if (count > 1) {
if (filter.getSkip() > 0) {
prev_skip = Math.max((filter.getSkip() - 1), 0);
prev_limit = filter.getLimit();
pagePrev = url + "?search=" + searchCondition + "&skip=" + prev_skip + "$limit=" + prev_limit;
} else{
result.setPaging(null);
return null;
}
if (count > (filter.getLimit() + filter.getSkip())) {
next_skip = (filter.getSkip() + filter.getLimit());
next_limit = filter.getLimit();
pageNext = url + "?search=" + searchCondition + "&skip=" + next_skip + "$limit=" + next_limit;
} else{
result.setPaging(null);
return null;
}
pagingOption.setPagePrev(pagePrev);
pagingOption.setPageNext(pageNext);
result.setPaging(pagingOption);
}
return pagingOption;
} catch (NullPointerException n)
{
n.printStackTrace();
return pagingOption;
}
}
call to generateLink method
return Result.ok(resultRow, generateLink(filter));
the ok response method in the Result class
public static Result ok(List<ResultRow> resultRows, Paging paging) {
if (paging != null) {
return new Result(resultRows, 200, "Completed", paging);
} else
return new Result(resultRows, 200, "Completed");
}
Underlying paging class
public class Paging implements Serializable {
public String getPagePrev() {
return pagePrev;
}
public void setPagePrev(String pagePrev) {
this.pagePrev = pagePrev;
}
#JsonProperty("pagePrev")
private String pagePrev;
public String getPageNext() {
return pageNext;
}
public void setPageNext(String pageNext) {
this.pageNext = pageNext;
}
#JsonProperty("pageNext")
private String pageNext;
}
but my test method is failing
#Test
public void search_allFilterParamsAreDefined_hp() throws Exception {
// given
Map<String, Serializable> searchConditions = singletonMap("shortName", "GO");
List<String> returnFields = asList("shortname", "gfcid");
String returnFieldsStr = returnFields.stream().collect(joining(","));
RequestFilter filter = RequestFilter.create(searchConditions, returnFieldsStr, 5, 10, true);
int resultSize = 20;
List<ResultRow> searchResult = getSearchResult(resultSize);
when(luceneSearchService.search(filter)).thenReturn(searchResult);
when(luceneSearchService.searchCount(filter)).thenReturn(resultSize);
// do
Result result = searchCoordinator.search(filter);
// verify
assertEquals(searchResult, result.getRows());
assertReturnFields(returnFields, result.getRows());
assertNotNull(result.getPaging());
Map<String, String> nextParams = getQueryParams(result.getPaging().getPageNext());
assertParam(nextParams, "search", toSimpleJsonString(searchConditions));
assertParam(nextParams, "skip", "15");
assertParam(nextParams, "limit", "10");
}

Related

Get metadata from shoutcast stream

I'm developing a radio app with multiple radios, the stream is playing fine. But I'm struggling to show artist and music playing at the moment.
This is the class I'm using to get metadata from shoutcast stream:
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class IcyStreamMeta<Message> {
protected URL streamUrl;
private Map<String, String> metadata;
private boolean isError;
public IcyStreamMeta(URL streamUrl) {
setStreamUrl(streamUrl);
isError = false;
}
/**
* Get artist using stream's title
*
* #return String
* #throws IOException
*/
public String getArtist() throws IOException {
Map<String, String> data = getMetadata();
if (!data.containsKey("StreamTitle"))
return "";
String streamTitle = data.get("StreamTitle");
String title = streamTitle.substring(0, streamTitle.indexOf("-"));
return title.trim();
}
/**
* Get title using stream's title
*
* #return String
* #throws IOException
*/
public String getTitle() throws IOException {
Map<String, String> data = getMetadata();
if (!data.containsKey("StreamTitle"))
return "";
String streamTitle = data.get("StreamTitle");
String artist = streamTitle.substring(streamTitle.indexOf("-")+1);
return artist.trim();
}
public Map<String, String> getMetadata() throws IOException {
if (metadata == null) {
refreshMeta();
}
return metadata;
}
public void refreshMeta() throws IOException {
retreiveMetadata();
}
private void retreiveMetadata() throws IOException {
URLConnection con = streamUrl.openConnection();
con.setRequestProperty("Icy-MetaData", "1");
con.setRequestProperty("Connection", "close");
con.setRequestProperty("Accept", null);
con.connect();
int metaDataOffset = 0;
Map<String, List<String>> headers = con.getHeaderFields();
InputStream stream = con.getInputStream();
if (headers.containsKey("icy-metaint")) {
// Headers are sent via HTTP
metaDataOffset = Integer.parseInt(headers.get("icy-metaint").get(0));
} else {
// Headers are sent within a stream
StringBuilder strHeaders = new StringBuilder();
char c;
while ((c = (char)stream.read()) != -1) {
strHeaders.append(c);
if (strHeaders.length() > 5 && (strHeaders.substring((strHeaders.length() - 4), strHeaders.length()).equals("\r\n\r\n"))) {
// end of headers
break;
}
}
// Match headers to get metadata offset within a stream
Pattern p = Pattern.compile("\\r\\n(icy-metaint):\\s*(.*)\\r\\n");
Matcher m = p.matcher(strHeaders.toString());
if (m.find()) {
metaDataOffset = Integer.parseInt(m.group(2));
}
}
// In case no data was sent
if (metaDataOffset == 0) {
isError = true;
return;
}
// Read metadata
int b;
int count = 0;
int metaDataLength = 4080; // 4080 is the max length
boolean inData = false;
StringBuilder metaData = new StringBuilder();
// Stream position should be either at the beginning or right after headers
while ((b = stream.read()) != -1) {
count++;
// Length of the metadata
if (count == metaDataOffset + 1) {
metaDataLength = b * 16;
}
if (count > metaDataOffset + 1 && count < (metaDataOffset + metaDataLength)) {
inData = true;
} else {
inData = false;
}
if (inData) {
if (b != 0) {
metaData.append((char)b);
}
}
if (count > (metaDataOffset + metaDataLength)) {
break;
}
}
// Set the data
metadata = IcyStreamMeta.parseMetadata(metaData.toString());
// Close
stream.close();
}
public boolean isError() {
return isError;
}
public URL getStreamUrl() {
return streamUrl;
}
public void setStreamUrl(URL streamUrl) {
this.metadata = null;
this.streamUrl = streamUrl;
this.isError = false;
}
public static Map<String, String> parseMetadata(String metaString) {
Map<String, String> metadata = new HashMap();
String[] metaParts = metaString.split(";");
Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'([^\\']*)\\'$");
Matcher m;
for (int i = 0; i < metaParts.length; i++) {
m = p.matcher(metaParts[i]);
if (m.find()) {
metadata.put(m.group(1), m.group(2));
}
}
return metadata;
}
}
And the method on MainActivity to get the metadata every 10 seconds
private void getMeta()
{
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
try {
IcyStreamMeta icy = new IcyStreamMeta(new URL(RadiophonyService.getRadioURL()));
final String data = icy.getArtist() + " - " + icy.getTitle();
final TextView meta = (TextView) findViewById(R.id.now_playing);
runOnUiThread(new Runnable() {
public void run() {
meta.setText(data);
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, 10000);
}
Initially, when I select one station, it plays but does not show metadata and when I select another station the app crashes with this runtime error:
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.app, PID: 23597
java.lang.StringIndexOutOfBoundsException: length=0; regionStart=0; regionLength=-1
at java.lang.String.startEndAndLength(String.java:504)
at java.lang.String.substring(String.java:1333)
at com.example.app.utilities.IcyStreamMeta.getArtist(IcyStreamMeta.java:41)
at com.example.app.activities.MainActivity$8.run(MainActivity.java:306)
at java.util.Timer$TimerImpl.run(Timer.java:284)
I'm stuck at this for days, and I tried other solutions but nothing works!
Looks like this line
String title = streamTitle.substring(0, streamTitle.indexOf("-"));
is the culprit.
If streamTitle does not have a dash in its title, indexOf() will return a -1, and substring() is choking because you can't have an end index less than the start index.
Maybe you need something like this:
int pos = streamTitle.indexOf("-");
String title = (pos == -1) ? streamTitle : streamTitle.substring(0, pos);

Struts 2 pagination,query syntax error

I have an error while I am getting data from database.It looks like everything is fine but i get this error?Why?
Full Error
(org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: 0 near line 1, column 47 [from com.project.Data limit 0,10] (Encoded))
DataAction.java
/**
* To list Data
*
* #return String
*/
public String listThrowException() {
try{
//Getting Total records count from database...
pagination.setPreperties(facade.getDataCount());
//Getting data list from database
listData = facade.listData(pagination);
//Setting number of records in the particular page
pagination.setPage_records(listData.size());
logger.info(Logger.EVENT_SUCCESS,
"successfully viewed data list!");
}catch (Exception e) {
logger.error(
Logger.EVENT_FAILURE,
"could not view data list, error: *"
+ e.getMessage() + "*");
}
return "success";
}
DataService.java
#Override
public List<Data> listData(Pagination pagination){
List<Data> list=new ArrayList<Data>();
Query query;
try {
String excQuery = "from Data limit ";
excQuery = pagination.getSQLQuery(excQuery);
query = em.createQuery(excQuery);
list = query.getResultList();
} catch (Exception e) {
logger.error(Logger.EVENT_FAILURE, e.getMessage());
return null;
}
return list;
}
Pagination.java
public class Pagination {
private long page_size = 0;
private int page_number = 0;
private long total_records = 0;
private int page_records = 0;
private long start = 0;
private long end = 0;
private int total_pages = 0;
public void setPreperties(long l){
total_records = l;
total_pages = (int) Math.ceil(((double)l / (double)page_size));
start = ((page_size * page_number) - page_size);
end = page_size*page_number;
if(page_size==0){
start = 0;
end = l;
total_pages = 1;
}
}
public Pagination(int page_size, int page_number) {
this.page_number = page_number;
this.page_size = page_size;
}
public String getSQLQuery(String query) {
query += getStart() + "," + getEnd();
return query;
}
///get and set methods
}
My problem is that "limit" in Hql it is not supported.I use setFirstResults() setMaxResults() and it works.My question is a good example for implementing paginaton in your page. I refer this tutorial http://prathap-puppala.blogspot.com/2011/06/struts-2-pagination-example.html I change only DateService.java** Good Luck!
DateService.java
#Override
public List<Data> listData(Pagination pagination){
List<Data> list=new ArrayList<Data>();
Query query;
try {
String excQuery = "from Data ";
query = em.createQuery(excQuery).
setFirstResult(pagination.getStart()).setMaxResults(pagination.getEnd());
query = em.createQuery(excQuery);
list = query.getResultList();
} catch (Exception e) {
logger.error(Logger.EVENT_FAILURE, e.getMessage());
return null;
}
return list;
}

How to compare the Json responedata and sql data in java

I written the code for Post data in webservice and it sends response data and code. Same data I am posting in the sql using select query. My scenario is I need to check responsedata and sql data are same or not.
Mysql.java
public ArrayList<DisputeSummaryarraylistobject> connectSqlconnectiondisputesummary() throws SQLException, ClassNotFoundException{
ArrayList<DisputeSummaryarraylistobject> DisputeSummaryarraylistobjectlist = new ArrayList<DisputeSummaryarraylistobject>();
String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
"databaseName=AdventureWorks;user=UserName;password=*****";
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
sqlConnection = DriverManager.getConnection(connectionUrl);
String sqlStatement = "Enter the sql statement";
stateMent = sqlConnection.createStatement();
resultSet = stateMent.executeQuery(sqlStatement);
DisputeSummaryarraylistobject disputeSummaryarraylistobject = new DisputeSummaryarraylistobject();
while (resultSet.next())
{
disputeSummaryarraylistobject.setchargeback(resultSet.getInt("chargeback"));
disputeSummaryarraylistobject.setactiveDisputes(resultSet.getInt("activeDisputes"));
disputeSummaryarraylistobject.setrecentUpdates(resultSet.getInt("recentUpdates"));
disputeSummaryarraylistobject.setrecentlyClosed(resultSet.getInt("recentlyClosed"));
disputeSummaryarraylistobject.setresponseRequired(resultSet.getInt("responseRequired"));
disputeSummaryarraylistobject.inProgress(resultSet.getInt("inProgress"));
disputeSummaryarraylistobject.setclosedInFavor(resultSet.getInt("closedInFavor"));
disputeSummaryarraylistobject.setclosedChargebacks(resultSet.getInt("closedChargebacks"));
DisputeSummaryarraylistobjectlist.add(disputeSummaryarraylistobject);
}
}
catch (Exception e) {
e.printStackTrace();
}
return DisputeSummaryarraylistobjectlist;
}
}
Myjsonresponse.java
public void convertResponseoutputtojsonobject() throws IOException, ParseException {
logger.info("convertResponseoutputtojsonobject() : BEGINs ");
JSONParser jsonParser = new JSONParser();
Object object = jsonParser.parse(response.toString());
JSONArray jsArray = (JSONArray) object;
Iterator<JSONObject> iterator = jsArray.iterator();
while (iterator.hasNext()) {
JSONObject jsonObj = iterator.next();
list.add(jsonObj);
}
logger.info(list.toString());
logger.info("convertResponseoutputtojsonobject() : ENDS ");
}
output : response data will be stored in the list.tostring() with formart
{
"chargeback": 5,
"activeDisputes": 12,
"recentUpdates": 10,
"recentlyClosed": 12,
"responseRequired": 8,
"inProgress": 4,
"closedInFavor": 4,
"closedChargebacks": 8
}
I need compare the list.tostring values and sql values. Anyone can give a solution how to compare?
If you have a DisputeSummaryarraylistobject with proper equals and hashcode method then you can use Gson to parse the json data directly to the object and do a comparision. Eg.
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class JsonCompare {
public static void main(String[] args) {
String jsonData = "{ \"chargeback\": 5, \"activeDisputes\": 12, \"recentUpdates\": 10, \"recentlyClosed\": 12, \"responseRequired\": 8, \"inProgress\": 4, \"closedInFavor\": 4, \"closedChargebacks\": 8 }";
Gson gson = new Gson();
DisputeSummaryarraylistobject webserviceObject = gson.fromJson(jsonData, DisputeSummaryarraylistobject.class);
//DDObject
DisputeSummaryarraylistobject dbObject = new DisputeSummaryarraylistobject();
dbObject.setChargeback(5);
dbObject.setActiveDisputes(12);
dbObject.setRecentUpdates(10);
dbObject.setRecentlyClosed(12);
dbObject.setResponseRequired(8);
dbObject.setInProgress(4);
dbObject.setClosedInFavor(4);
dbObject.setClosedChargebacks(8);
System.out.println(webserviceObject.equals(dbObject));
// If you have an array of objects
String jsonDataArray = "[ { \"chargeback\": 5, \"activeDisputes\": 12, \"recentUpdates\": 10, \"recentlyClosed\": 12, \"responseRequired\": 8, \"inProgress\": 4, \"closedInFavor\": 4, \"closedChargebacks\": 8 }, "
+ " { \"chargeback\": 6, \"activeDisputes\": 7, \"recentUpdates\": 8, \"recentlyClosed\": 2, \"responseRequired\": 5, \"inProgress\": 14, \"closedInFavor\": 14, \"closedChargebacks\": 5 } ]";
Type listType = new TypeToken<List<DisputeSummaryarraylistobject>>() {}.getType();
List<DisputeSummaryarraylistobject> disputeSummaryArraylistobjectList = gson.fromJson(jsonDataArray, listType);
System.out.println(disputeSummaryArraylistobjectList);
//Now you can iterate through the array and compare the objects with equals.
}
}
A sample Dispute Summary Object
public class DisputeSummaryarraylistobject {
private int chargeback;
private int activeDisputes;
private int recentUpdates;
private int recentlyClosed;
private int responseRequired;
private int inProgress;
private int closedInFavor;
private int closedChargebacks;
public int getChargeback() {
return chargeback;
}
public void setChargeback(int chargeback) {
this.chargeback = chargeback;
}
public int getActiveDisputes() {
return activeDisputes;
}
public void setActiveDisputes(int activeDisputes) {
this.activeDisputes = activeDisputes;
}
public int getRecentUpdates() {
return recentUpdates;
}
public void setRecentUpdates(int recentUpdates) {
this.recentUpdates = recentUpdates;
}
public int getRecentlyClosed() {
return recentlyClosed;
}
public void setRecentlyClosed(int recentlyClosed) {
this.recentlyClosed = recentlyClosed;
}
public int getResponseRequired() {
return responseRequired;
}
public void setResponseRequired(int responseRequired) {
this.responseRequired = responseRequired;
}
public int getInProgress() {
return inProgress;
}
public void setInProgress(int inProgress) {
this.inProgress = inProgress;
}
public int getClosedInFavor() {
return closedInFavor;
}
public void setClosedInFavor(int closedInFavor) {
this.closedInFavor = closedInFavor;
}
public int getClosedChargebacks() {
return closedChargebacks;
}
public void setClosedChargebacks(int closedChargebacks) {
this.closedChargebacks = closedChargebacks;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + activeDisputes;
result = prime * result + chargeback;
result = prime * result + closedChargebacks;
result = prime * result + closedInFavor;
result = prime * result + inProgress;
result = prime * result + recentUpdates;
result = prime * result + recentlyClosed;
result = prime * result + responseRequired;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DisputeSummaryarraylistobject other = (DisputeSummaryarraylistobject) obj;
if (activeDisputes != other.activeDisputes)
return false;
if (chargeback != other.chargeback)
return false;
if (closedChargebacks != other.closedChargebacks)
return false;
if (closedInFavor != other.closedInFavor)
return false;
if (inProgress != other.inProgress)
return false;
if (recentUpdates != other.recentUpdates)
return false;
if (recentlyClosed != other.recentlyClosed)
return false;
if (responseRequired != other.responseRequired)
return false;
return true;
}
#Override
public String toString() {
return "DisputeSummaryarraylistobject [chargeback=" + chargeback
+ ", activeDisputes=" + activeDisputes + ", recentUpdates="
+ recentUpdates + ", recentlyClosed=" + recentlyClosed
+ ", responseRequired=" + responseRequired + ", inProgress="
+ inProgress + ", closedInFavor=" + closedInFavor
+ ", closedChargebacks=" + closedChargebacks + "]";
}
}
Instead of checking if the JSON documents are the same in your client, you could also do that directly in SQL:
select cast(? as jsonb) = (select jsonb_agg(jsonb_build_object(...)) from ...)
It may not be the right approach for you, but just a thought that is often overlooked.

Getting metadata from SHOUTcast using IcyStreamMeta

I am writing an app for Android that grabs meta data from SHOUTcast mp3 streams. I am using a pretty nifty class I found online that I slightly modified, but I am still having 2 problems.
1) I have to continuously ping the server to update the metadata using a TimerTask. I am not fond of this approach but it was all I could think of.
2) There is a metric tonne of garbage collection while my app is running. Removing the TimerTask got rid of the garbage collection issue so I am not sure if I am just doing it wrong or if this is normal.
Here is the class I am using:
public class IcyStreamMeta {
protected URL streamUrl;
private Map<String, String> metadata;
private boolean isError;
public IcyStreamMeta(URL streamUrl) {
setStreamUrl(streamUrl);
isError = false;
}
/**
* Get artist using stream's title
*
* #return String
* #throws IOException
*/
public String getArtist() throws IOException {
Map<String, String> data = getMetadata();
if (!data.containsKey("StreamTitle"))
return "";
try {
String streamTitle = data.get("StreamTitle");
String title = streamTitle.substring(0, streamTitle.indexOf("-"));
return title.trim();
}catch (StringIndexOutOfBoundsException e) {
return "";
}
}
/**
* Get title using stream's title
*
* #return String
* #throws IOException
*/
public String getTitle() throws IOException {
Map<String, String> data = getMetadata();
if (!data.containsKey("StreamTitle"))
return "";
try {
String streamTitle = data.get("StreamTitle");
String artist = streamTitle.substring(streamTitle.indexOf("-")+1);
return artist.trim();
} catch (StringIndexOutOfBoundsException e) {
return "";
}
}
public Map<String, String> getMetadata() throws IOException {
if (metadata == null) {
refreshMeta();
}
return metadata;
}
public void refreshMeta() throws IOException {
retreiveMetadata();
}
private void retreiveMetadata() throws IOException {
URLConnection con = streamUrl.openConnection();
con.setRequestProperty("Icy-MetaData", "1");
con.setRequestProperty("Connection", "close");
//con.setRequestProperty("Accept", null);
con.connect();
int metaDataOffset = 0;
Map<String, List<String>> headers = con.getHeaderFields();
InputStream stream = con.getInputStream();
if (headers.containsKey("icy-metaint")) {
// Headers are sent via HTTP
metaDataOffset = Integer.parseInt(headers.get("icy-metaint").get(0));
} else {
// Headers are sent within a stream
StringBuilder strHeaders = new StringBuilder();
char c;
while ((c = (char)stream.read()) != -1) {
strHeaders.append(c);
if (strHeaders.length() > 5 && (strHeaders.substring((strHeaders.length() - 4), strHeaders.length()).equals("\r\n\r\n"))) {
// end of headers
break;
}
}
// Match headers to get metadata offset within a stream
Pattern p = Pattern.compile("\\r\\n(icy-metaint):\\s*(.*)\\r\\n");
Matcher m = p.matcher(strHeaders.toString());
if (m.find()) {
metaDataOffset = Integer.parseInt(m.group(2));
}
}
// In case no data was sent
if (metaDataOffset == 0) {
isError = true;
return;
}
// Read metadata
int b;
int count = 0;
int metaDataLength = 4080; // 4080 is the max length
boolean inData = false;
StringBuilder metaData = new StringBuilder();
// Stream position should be either at the beginning or right after headers
while ((b = stream.read()) != -1) {
count++;
// Length of the metadata
if (count == metaDataOffset + 1) {
metaDataLength = b * 16;
}
if (count > metaDataOffset + 1 && count < (metaDataOffset + metaDataLength)) {
inData = true;
} else {
inData = false;
}
if (inData) {
if (b != 0) {
metaData.append((char)b);
}
}
if (count > (metaDataOffset + metaDataLength)) {
break;
}
}
// Set the data
metadata = IcyStreamMeta.parseMetadata(metaData.toString());
// Close
stream.close();
}
public boolean isError() {
return isError;
}
public URL getStreamUrl() {
return streamUrl;
}
public void setStreamUrl(URL streamUrl) {
this.metadata = null;
this.streamUrl = streamUrl;
this.isError = false;
}
public static Map<String, String> parseMetadata(String metaString) {
Map<String, String> metadata = new HashMap<String, String>();
String[] metaParts = metaString.split(";");
Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'([^\\']*)\\'$");
Matcher m;
for (int i = 0; i < metaParts.length; i++) {
m = p.matcher(metaParts[i]);
if (m.find()) {
metadata.put((String)m.group(1), (String)m.group(2));
}
}
return metadata;
}
}
And here is my timer:
private void getMeta() {
timer.schedule(new TimerTask() {
public void run() {
try {
icy = new IcyStreamMeta(new URL(stationUrl));
runOnUiThread(new Runnable() {
public void run() {
try {
artist.setText(icy.getArtist());
title.setText(icy.getTitle());
} catch (IOException e) {
e.printStackTrace();
} catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
},0,5000);
}
Much appreciation for any assistance!
I've replaced the IcyStreamMeta class in my program and am getting the meta data from the 7.html file that is a part of the SHOUTcast spec. Far less data usage and all that so I feel it is a better option.
I am still using the TimerTask, which is acceptable. There is practically no GC any more and I am happy with using 7.html and a little regex. :)

Function based expression message renderer

I'm doing a simple MessageRenderer.
It's specification:
Render message based on an Context (it's a map that's contains all key/value pair parameters)
Supports simple render such as: Your username is << username >>. Assume username in the context is barcelona and the result will be Your username is Barcelona.
Supported function-like object. Example: Current time is << now() >>, now(): is an object that will returns a string of current date time. And result will be: Current time is 2011-05-30
Each parameter of function can also be templated: Current time is << now( << date_format >> ) >> . This template returns a string of current date time with format is the value of key 'date_format' retrieved from the Context. Assume date_format in Context is dd/MM/yyyy and the result will be: Current time is 30/05/2011
Each parameter of function can also be templated with a different method call: Time is << now_locale ( << getLocale() >> ). Assume that getLocale() is an function object that will be return a locale is en_US and the result will be: Time is 2011/05/30 11:20:34 PM
Template can be nested. Example: Your user name is << << username >> >>. It means, Key username has value param1, Key param1 has value is barcelona so the final result will be: Your user name is Barcelona.
My classes and interfaces:
RenderContext.java
public interface RenderContext {
public String getParameter(String key);
}
MessageRenderer.java
public interface MessageRenderer {
public String render(String s, RenderContext... context);
}
MethodExpressionEvaluator.java
// Using this class to implements the method evaluation, such as now(), now_locale()
public interface MethodExpressionEvaluator {
public String evaluate(String[] methodParams, RenderContext... context);
}
AbstractMessageRenderer.java
public abstract class AbstractMessageRenderer implements MessageRenderer {
public static final String DEFAULT_NULL = "###";
public static final String PLACEHOLDER_START_TOKEN = "<<";
public static final String PLACEHOLDER_END_TOKEN = ">>";
protected int lenPlaceholderStartToken = 0;
protected int lenPlaceholderEndToken = 0;
protected String nullToken;
protected String placeholderStartToken;
protected String placeholderEndToken;
protected boolean escape = true;
public AbstractMessageRenderer() {
placeholderStartToken = PLACEHOLDER_START_TOKEN;
placeholderEndToken = PLACEHOLDER_END_TOKEN;
lenPlaceholderStartToken = placeholderStartToken.length();
lenPlaceholderEndToken = placeholderEndToken.length();
nullToken = DEFAULT_NULL;
}
public String getNullToken() {
return nullToken;
}
public void setNullToken(String defaultNull) {
this.nullToken = defaultNull;
}
public String getPlaceholderStartToken() {
return placeholderStartToken;
}
public void setPlaceholderStartToken(String placeholderStartToken) {
this.placeholderStartToken = placeholderStartToken;
lenPlaceholderStartToken = placeholderStartToken.length();
}
public String getPlaceholderEndToken() {
return placeholderEndToken;
}
public void setPlaceholderEndToken(String placeholderEndToken) {
this.placeholderEndToken = placeholderEndToken;
lenPlaceholderEndToken = placeholderEndToken.length();
}
public boolean isEscape() {
return escape;
}
public boolean getEscape() {
return escape;
}
public void setEscape(boolean escape) {
this.escape = escape;
}
public String getParam(String key, RenderContext... context) {
if(context != null)
{
for(RenderContext param:context)
{
if(param != null)
{
String value = param.getParameter(key);
if(!StringUtil.isEmpty(value))
{
return value;
}
}
}
}
return nullToken;
}
public String render(String s, RenderContext... context) {
// handle trivial cases of empty template or no placeholders
if (s == null)
{
Log4j.app.debug("Message is null in template. Cannot render null message.");
return nullToken;
}
if (context == null)
{
Log4j.app.debug("RenderContext is null. Cannot render message with null RenderContext.");
return nullToken;
}
if (s.indexOf(placeholderStartToken) < 0)
{
return s;
}
String msg = nullToken;
try
{
// private int renderTemplate(Renderable r, String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end,boolean escapes)
msg = doRender(s, context);
}
catch (Exception e)
{
Log4j.app.error("Exception in rendering template: " + e.getMessage(), e);
return nullToken;
}
return msg;
}
protected abstract String doRender(String s, RenderContext... context);
}
MethodExpressionRenderer.java
public class MethodExpressionRenderer extends AbstractMessageRenderer {
private boolean inSingleQuote = false;
private boolean inDoubleQuote=false;
private int placeholders;
private Stack<String> methodStack;
private String[] endTokens;
private String marker;
private List<String> methodParams;
private String prefix = "&";
public MethodExpressionRenderer() {
super();
methodStack = new Stack<String>();
marker = ",";
endTokens = new String[] { placeholderEndToken, marker, "(", ")" };
methodParams = new ArrayList<String>();
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getMarker() {
return marker;
}
public void setMarker(String marker) {
this.marker = marker;
endTokens = new String[] { placeholderEndToken, marker };
}
#Override
public void setPlaceholderEndToken(String placeholderEndToken) {
super.setPlaceholderEndToken(placeholderEndToken);
endTokens = new String[] { placeholderEndToken, marker };
}
protected String doRender(String s, RenderContext... context) {
StringBuffer sb = new StringBuffer();
try
{
renderTemplate(s, sb, nullToken, 0, endTokens, null, context);
}
catch (Exception e)
{
Log4j.app.error("Exception in rendering method expression message emplate: " + e.getMessage(), e);
return nullToken;
}
return sb.toString();
}
private int renderTemplate(String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end, RenderContext... context) {
int len = src.length();
while (i < len)
{
char c = src.charAt(i);
if (escape)
{
if (c=='\\')
{
i++;
char ch = src.charAt(i);
if(inSingleQuote)
{
if(ch=='\'')
{
inSingleQuote=false;
}
}
else if(inDoubleQuote)
{
if(ch=='"')
{
inDoubleQuote=false;
}
}
else
{
if(ch=='\'')
{
inSingleQuote=true;
}
else if(ch=='"')
{
inDoubleQuote=true;
}
}
dst.append(ch);
i++;
continue;
}
}
if(inSingleQuote)
{
if(c=='\'')
{
inSingleQuote=false;
}
}
else if(inDoubleQuote)
{
if(c=='"')
{
inDoubleQuote=false;
}
}
else
{
if(c=='\'')
{
inSingleQuote=true;
}
else if(c=='"')
{
inDoubleQuote=true;
}
}
// check for end marker
if (marks != null && !inSingleQuote && !inDoubleQuote)
{
for (int m = 0; m < marks.length; m++)
{
// If one of markers found
if (src.regionMatches(i, marks[m], 0, marks[m].length()))
{
// return marker if required
if (end != null)
{
end.append(marks[m]);
}
return i+marks[m].length();
}
}
}
// check for start of placeholder
if (src.regionMatches(i, placeholderStartToken, i, lenPlaceholderStartToken))
{
synchronized(this)
{
++placeholders;
}
i = renderPlaceholder(src, dst, nil, i, new ArrayList<String>(), context);
continue;
}
// just add plain character
if(c != '\'' && c!= '"')
{
dst.append(c);
}
i++;
}
return i;
}
private int renderPlaceholder(String src, StringBuffer dst, String nil, int i, List<String> params, RenderContext... context){
StringBuffer token = new StringBuffer(); // placeholder token
StringBuffer end = new StringBuffer(); // placeholder end marker
String value;
i = renderTemplate(src, token, nil, i+lenPlaceholderStartToken, endTokens, end);
String sToken = token.toString().trim();
String sEnd = end.toString().trim();
boolean isFunction = sEnd.equals("(");
// This is method name
if(isFunction && placeholders > methodStack.size())
{ // Method
synchronized(this)
{
methodStack.push(sToken); // put method into stack
}
}
else if(!isFunction && (methodStack.size()==0) && sEnd.equals(placeholderEndToken)) // Single template param such as <<param>>
{
value = getParam(sToken, context);
if(value != null)
{
if(value.trim().startsWith(placeholderStartToken))
{
value = render(src, context);
}
dst.append(value);
return i;
}
}
// TODO: Process method parameters to invoke
//.... ?????????
// Found end method token ')'
// Pop method out of stack to invoke
if ( (methodStack.size() >0) && (sEnd.length() == 0 || sEnd.equals(")")))
{
String method = null;
synchronized(this)
{
// Pop method out of stack to invoke
method = methodStack.pop();
--placeholders;
dst.append(invokeMethodEvaluator(method, methodParams.toArray(new String[0]), context));
methodParams.clear();
}
}
return i;
}
// Currently this method just implement to test so it just printout the method name
// and its parameter
// We can register MethodExpressionEvaluator to process
protected String invokeMethodEvaluator(String method, String[] params, RenderContext... context){
StringBuffer result = new StringBuffer();
result.append("[ ")
.append(method)
.append(" ( ");
if(params != null)
{
for(int i=0; i<params.length; i++)
{
result.append(params[i]);
if(i != params.length-1)
{
result.append(" , ");
}
}
}
result.append(" ) ")
.append(" ] ");
return result.toString();
}
}
We can easily register more method to the renderer to invoke. Each method will be an object and can be reused. But I'm in trouble how to resolve the nested method parameter. Can anyone give me an advice how we can process nested template of method parameter to invoke??? The line has TODO. Will my code in on the right way???
When you evaluate something like << count( << getTransId() >> ) >> you can either:
perform direct-evaluation as you parse, and push each function onto a stack, so that once you've evaluated getTransId() you pop the stack and use the return value (from the stack) as an argument for count(), or
you can build a parse tree to represent all the function calls that will be made, and then evaluate your parse tree after building it. (Building a tree probably doesn't buy you anything; since you're writing a template engine there is probably no high-level tree operation 'optimizations' that you could perform.)
An excellent little book I really enjoyed was Language Implementation Patterns by Parr. He walks through building simple to complex languages, and covers decisions like this in some depth. (Yes, he uses the ANTLR parser generator throughout, but your code looks like you're familiar enough with hand-generated parsers that different tools won't be a distraction for you.)
I found the bug and fixed it.
This is my new source:
// AbstractMethodExpressionRenderer.java
public class AbstractMethodExpressionRenderer extends AbstractMessageRenderer {
private boolean inSingleQuote = false;
private boolean inDoubleQuote=false;
private Stack<MethodExpressionDescriptor> functionStack;
private String[] endTokens;
private String marker;
private String prefix = "~";
public AbstractMethodExpressionRenderer() {
super();
functionStack = new Stack<MethodExpressionDescriptor>();
marker = ",";
endTokens = new String[] { placeholderEndToken, "(", ")", };
}
private class MethodExpressionDescriptor {
public List<String> params;
public String function;
public MethodExpressionDescriptor() {
params = new ArrayList<String>();
}
public MethodExpressionDescriptor(String name) {
this();
this.function = name;
}
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getMarker() {
return marker;
}
public void setMarker(String marker) {
this.marker = marker;
endTokens = new String[] { placeholderEndToken, marker };
}
#Override
public void setPlaceholderEndToken(String placeholderEndToken) {
super.setPlaceholderEndToken(placeholderEndToken);
endTokens = new String[] { placeholderEndToken, marker };
}
protected String doRender(String s, RenderContext... context) {
StringBuffer sb = new StringBuffer();
try
{
renderTemplate(s, sb, nullToken, 0, endTokens, null, context);
}
catch (Exception e)
{
Log4j.app.error("Exception in rendering method expression message emplate: " + e.getMessage(), e);
return nullToken;
}
return sb.toString();
}
private int renderTemplate(String src, StringBuffer dst, String nil, int i, String[] marks, StringBuffer end, RenderContext... context) {
int len = src.length();
while (i < len)
{
char c = src.charAt(i);
if (escape)
{
if (c=='\\')
{
i++;
char ch = src.charAt(i);
if(inSingleQuote)
{
if(ch=='\'')
{
inSingleQuote=false;
}
}
else if(inDoubleQuote)
{
if(ch=='"')
{
inDoubleQuote=false;
}
}
else
{
if(ch=='\'')
{
inSingleQuote=true;
}
else if(ch=='"')
{
inDoubleQuote=true;
}
}
dst.append(ch);
i++;
continue;
}
}
if(inSingleQuote)
{
if(c=='\'')
{
inSingleQuote=false;
}
}
else if(inDoubleQuote)
{
if(c=='"')
{
inDoubleQuote=false;
}
}
else
{
if(c=='\'')
{
inSingleQuote=true;
}
else if(c=='"')
{
inDoubleQuote=true;
}
}
// check for end marker
if (marks != null && !inSingleQuote && !inDoubleQuote)
{
for (int m = 0; m < marks.length; m++)
{
// If one of markers found
if (src.regionMatches(i, marks[m], 0, marks[m].length()))
{
// return marker if required
if (end != null)
{
end.append(marks[m]);
}
return i+marks[m].length();
}
}
}
// check for start of placeholder
if (src.regionMatches(i, placeholderStartToken, 0, lenPlaceholderStartToken))
{
i = renderPlaceholder(src, dst, nil, i, new ArrayList<String>(), context);
continue;
}
// just add plain character
if(c != '\'' && c!= '"')
{
dst.append(c);
}
i++;
}
return i;
}
/**
* Render a placeholder as follows:
*
* <<key>>: Simple render, key value map
* <<function(<<param1>>, <<param2>>)>> : Function object render
*
* #param src
* #param dst
* #param nil
* #param i
* #param params
* #param context
* #return
*/
private int renderPlaceholder(String src, StringBuffer dst, String nil, int i, List<String> params, RenderContext... context){
StringBuffer token = new StringBuffer(); // placeholder token
StringBuffer end = new StringBuffer(); // placeholder end marker
String value = null;
// Simple key
i = renderTemplate(src, token, nil, i+lenPlaceholderStartToken, endTokens, end, context);
String sToken = token.toString().trim();
String sEnd = end.toString().trim();
// This is method name
if(sEnd.equals("("))
{ // Method
functionStack.add(new MethodExpressionDescriptor(sToken));
}
else // Try to resolve value
{
if(sToken.startsWith(placeholderStartToken))
{
value = render(sToken, context);
}
else if(sToken.startsWith(prefix))
{
if(functionStack.size() > 0)
{
functionStack.peek().params.add(sToken.substring(1));
}
return i;
}
else
{
value = getParam(sToken, context);
}
}
if (sEnd.length() == 0 || sEnd.equals(placeholderEndToken))
{
// No method found but found the end of placeholder token
if(functionStack.size() == 0)
{
if(value != null)
{
dst.append(value);
}
else
{
dst.append(nil);
}
}
else
{
functionStack.peek().params.add(value);
}
}
else
{
if(value != null)
{
value = value.trim();
}
if(end.substring(0, 1).equals("(") ||
end.substring(0, 1).equals(marker))
{
// right hand side is remainder of placeholder
StringBuffer tmp = new StringBuffer();
end = new StringBuffer();
i = renderTemplate(src, tmp, nil, i, endTokens, end, context);
}
if(end.substring(0, 1).equals(")"))
{
if ( functionStack.size() > 0 )
{
// Pop method out of stack to invoke
MethodExpressionDescriptor descriptor = functionStack.pop();
if(functionStack.size() > 0 )
{
functionStack.peek().params.add(invokeMethodEvaluator(descriptor.function, descriptor.params.toArray(new String[0]), context));
}
else
{
dst.append(invokeMethodEvaluator(descriptor.function, descriptor.params.toArray(new String[0]), context));
}
end = new StringBuffer();
StringBuffer tmp = new StringBuffer();
i = renderTemplate(src, tmp, nil, i, endTokens, end, context);
}
}
}
return i;
}
protected String invokeMethodEvaluator(String method, String[] params, RenderContext... context){
StringBuffer result = new StringBuffer();
result.append("[ ")
.append(method)
.append(" ( ");
if(params != null)
{
for(int i=0; i<params.length; i++)
{
result.append(params[i]);
if(i != params.length-1)
{
result.append(" , ");
}
}
}
result.append(" ) ")
.append(" ] ");
return result.toString();
}
}

Categories

Resources