Java tutorial
/* * ============================================================= * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com) * ============================================================= * License Information: http://www.edgenius.com/licensing/edgenius/2.0/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2.0 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * http://www.gnu.org/licenses/gpl.txt * * **************************************************************** */ package com.edgenius.wiki.gwt.client; import java.util.Date; import com.allen_sauer.gwt.log.client.Log; import com.edgenius.wiki.gwt.client.i18n.Msg; import com.edgenius.wiki.gwt.client.model.GeneralModel; import com.edgenius.wiki.gwt.client.server.ClientAccessDeniedException; import com.edgenius.wiki.gwt.client.server.ClientAuthenticationException; import com.edgenius.wiki.gwt.client.server.utils.ErrorCode; import com.edgenius.wiki.gwt.client.server.utils.EscapeUtil; import com.edgenius.wiki.gwt.client.server.utils.GwtUtils; import com.edgenius.wiki.gwt.client.server.utils.SharedConstants; import com.edgenius.wiki.gwt.client.widgets.MessageWidget; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.http.client.URL; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.user.client.History; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; import com.google.gwt.user.client.rpc.InvocationException; import com.google.gwt.user.client.ui.FormPanel; import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Widget; /** * GwtUtils won't require any Class from GWT client side, like BaseEntryPoint etc. So here create * a pure client Util class which will compile with Gwt Client Code together. * @author Dapeng.Ni */ public class GwtClientUtils { static boolean obsoleteVerWin = false; /** * Most AsyncCallback.onSuccess() method will use this method to handle success. Except:<br> * EditPanel.SaveExitAsync class: need special handle for version conflict * * @param model * @return */ public static boolean preSuccessCheck(GeneralModel model, MessageWidget message) { //NOTE: current signupForm does not use this method, if add extra functions, please check it. //try to check if session timeout if (BaseEntryPoint.I != null) //LoginMain is not extend from BaseEntryPoint, then it should be null. BaseEntryPoint.I.isSessionExpired(model.loginUsername); if (ErrorCode.hasError(model)) { if (message != null) message.error(ErrorCode.getMessage(model.errorCode, model.errorMsg)); return false; } return true; } /** * @param obj error object * @return true, online exception, false, system is current offline. */ public static boolean processError(Throwable obj) { if (obj instanceof IncompatibleRemoteServiceException) { if (!obsoleteVerWin) { //only popup this message once, until user refresh page, otherwise, each RPC call will popup on, Dashboard may contain large //amount RCP call.... Window.alert(((IncompatibleRemoteServiceException) obj).getMessage()); obsoleteVerWin = true; } return true; } else if (obj instanceof ClientAuthenticationException) { String loginUrl = ((ClientAuthenticationException) obj).getLoginUrl(); if (AbstractEntryPoint.isOffline()) { Window.alert("auth exp"); } else { //append webcontext before loginURL gotoLogin(getRediectURL(loginUrl), getLocation()); } //return withError flag return true; } else if (obj instanceof ClientAccessDeniedException) { String errorUrl = ((ClientAccessDeniedException) obj).getErrorUrl(); if (AbstractEntryPoint.isOffline()) { Window.alert("Access denied."); } else { redirectByForm(errorUrl); } return true; } else if (obj instanceof InvocationException) { //IE, Opera, Safari will throw InvocationException if server is not available (or offline model) return false; } else if (obj instanceof RuntimeException) { //???Firefox will throw RuntimeException if server is not available (or offline model) return false; } Window.alert("Unexpected error " + obj); return true; } //JQuery dependence method - although slideUp(80) looks better, but in some case, loading bar can not be hidden. The sample is from //local Safari from view page to view template, it is so quick, less than 80ms! This causes hidden method is executed before loading complete! public static native void hideLoading(boolean hide) /*-{ if(hide){ $wnd.$('#loading').hide(); }else{ $wnd.$('#loading').show(); } }-*/; public static native void hidePagePin(boolean hide) /*-{ if(hide){ $wnd.$('#_page_pin').hide(); }else{ $wnd.$('#_page_pin').show(); } }-*/; public static native void gotoLogin(String loginUrl, String currLocation) /*-{ $wnd.callLogin(loginUrl,currLocation); }-*/; /** * @return */ public static Image logo() { Image logo = new Image(getBaseUrl() + "download?instance=logo"); logo.setStyleName(Css.LEFT); return logo; } /** * @param url */ public static void reload(String url) { if (BaseEntryPoint.I != null) { //try to use ajax request to reload the page if the page is already initialised: //This is NOT true if current page is in Login/Signup page(not that pop dialog) as LoginEntryPoint is not extend from BaseEntryPoint //However, if it is normal page, then here just retry parse token and reload with ajax request. BaseEntryPoint.I.reload(); } else { //some other URL, just send redirect, this may won't cause browser refresh page, if the given URL equals current URL. redirect(url); } } /** * @param event * @return */ public static String getFormResult(SubmitCompleteEvent event) { String results = event.getResults() == null ? "" : event.getResults().trim(); Log.info("Form submit result:" + results); //Test case: Page access denied, login then redirect... //http://foo/page#/abc & 123 <kk> title //& need unescapeHTML; <> looks works in FF, but not in Safari - no 100% sure URL.decodeComponent() is required. //Anyway, this is may have bug for page title which has some special character combination. But happen rarely, so //mark it as low results = URL.decodePathSegment(results); results = EscapeUtil.unescapeHTML(results); Log.info("Form submit result after decoded:" + results); //Some browser pre tag contain style info, so just check <pre rather than <pre> if (results.toLowerCase().startsWith("<pre")) { results = results.substring(results.indexOf(">") + 1); if (results.toLowerCase().endsWith("</pre>")) results = results.substring(0, results.length() - 6); } //handle redir request if (results.startsWith(SharedConstants.FORM_RET_HEADER + SharedConstants.FORM_RET_ACCESS_DENIED_EXP)) { gotoLogin(results.substring( (SharedConstants.FORM_RET_HEADER + SharedConstants.FORM_RET_ACCESS_DENIED_EXP).length()), getLocation()); return null; } if (results.startsWith(SharedConstants.FORM_RET_HEADER + SharedConstants.FORM_RET_AUTH_EXP)) { redirectByForm(results .substring((SharedConstants.FORM_RET_HEADER + SharedConstants.FORM_RET_AUTH_EXP).length())); return null; } return results; } /** * This method is for add page leaving confirmation message. Specially for editing content won't lose if page is redirect. */ public static native void onBeforeUnload(String msg)/*-{ $wnd.message = msg; if($wnd.attachEvent) { $wnd.attachEvent("onbeforeunload", $wnd.beforeUnload); }else { $wnd.onbeforeunload = $wnd.beforeUnload; } }-*/; public static native void clearBeforeUnload()/*-{ if($wnd.detachEvent) { $wnd.detachEvent("onbeforeunload", $wnd.beforeUnload); }else { $wnd.onbeforeunload = null; } }-*/; /** * get base context url: http://localhost/wibok/page/ to * http://localhost/wibok/ <BR> * Limited on on level deep * * @param moduleUrl * @return */ public static String getBaseUrl() { String moduleUrl = GWT.getModuleBaseURL(); return moduleUrl.substring(0, moduleUrl.substring(0, moduleUrl.length() - 1).lastIndexOf('/') + 1); } public static void redirect(String relativeUrl) { if (relativeUrl.startsWith("http://") || relativeUrl.startsWith("https://")) { redirectUrl(relativeUrl); } else redirectUrl(getBaseUrl() + relativeUrl); } public static void redirectByForm(String relativeUrl) { if (relativeUrl.startsWith("http://") || relativeUrl.startsWith("https://")) redirectUrlByForm(relativeUrl); else { if (relativeUrl.length() > 0 && relativeUrl.charAt(0) == '/') relativeUrl = relativeUrl.substring(1); redirectUrlByForm(getBaseUrl() + relativeUrl); } } public static String getRediectURL(String relativeUrl) { if (relativeUrl.startsWith("http://") || relativeUrl.startsWith("https://")) return relativeUrl; else { if (relativeUrl.length() > 0 && relativeUrl.charAt(0) == '/') relativeUrl = relativeUrl.substring(1); return getBaseUrl() + relativeUrl; } } public static native void redirectUrl(String url) /*-{ $wnd.open (url,"_self",null); }-*/; /** * Safari browser does not work by GwtUtils.redirect() if this method is called inside ajax return method. * User form submit instead of "location.href=newUrl" to fix redirect issue. * @param redirUrl */ public static native void redirectUrlByForm(String redirUrl) /*-{ $wnd.redirForm(redirUrl); }-*/; /** * @return */ public native static String getLocation() /*-{ return window.top.location.href; }-*/; /** * Use JQuery scrollTo plugin * It has 2 scenarios. First, by element ID, that any elements can be anchor, * such as <div id=XX>. Page Index macro just used this format. * * Second, by anchor, <a name=xxx> format. * @param anchor */ public static native void gotoAnchor(String anchorID) /*-{ if($wnd.$('#'+anchorID).length > 0){ $wnd.$.scrollTo('#'+anchorID,500,{margin:true}); }else if($wnd.$("a[name='"+anchorID+"']").length > 0){ $wnd.$.scrollTo("a[name='"+anchorID+"']",500,{margin:true}); } }-*/; /** * This method limited on, page only can has one form. Otherwise, the form * which added last will be submit. * * !!!!!!!!!!!!!! NOTE: this method must be refactor if you want to user it, * because you must must removeEventPreview() after this EnterSubmit finish * function. Otherwise, system scope short cut will lost function. * !!!!!!!!!!!!!! * * @param form */ public static void enableEnterSubmit(final FormPanel form) { // make user click enter in textbox and form automatically submit: // default action in HTML form, but GWT does not work Event.addNativePreviewHandler(new NativePreviewHandler() { public void onPreviewNativeEvent(NativePreviewEvent event) { if (!event.isCanceled() && event.getTypeInt() == Event.ONKEYDOWN) { int keyCode = event.getNativeEvent().getKeyCode(); // Return clicked if (keyCode == KeyCodes.KEY_ENTER) { form.submit(); event.cancel(); } } } }); } /** * Please don't use this method when you can use * <code>createUserPortrait</code> method. Because this method will force * refresh image from server every visit. * * @param username * @return */ public static Widget createUserPortraitByUsername(String username) { String imgUrl; if (username == null || username.trim().length() == 0) { // return default user portrait imgUrl = getBaseUrl() + "static/images/noportrait.jpg"; } else { imgUrl = getBaseUrl() + "download?user=" + URL.encodeQueryString(username) + "&refresh=" + System.currentTimeMillis(); } return new HTML("<img src=\"" + imgUrl + "\"" + " title=\"portrait\">"); } public static HTML buildDownloadURLWidget(String spaceUname, String filename, String nodeUuid, String version) { String url = buildAttachmentURL(spaceUname, filename, nodeUuid, version, true); //Must use double quote for URL, single quote may appear in spaceUname even aftere encoding... return new HTML( "<a href=\"" + url + "\" title='" + Msg.consts.download_attachment() + "'>" + filename + "</a>"); } /** * This method only can be use on client gwt program as it use GwtUtils.getBaseUrl() * @param spaceUname * @param nodeUuid * @param version * @return */ public static String buildAttachmentURL(String spaceUname, String filename, String nodeUuid, String version, boolean download) { String verParm = version != null ? ("&version=" + version) : ""; return new StringBuffer(getBaseUrl()).append("download?space=").append(URL.encodeQueryString(spaceUname)) .append("&uuid=").append(nodeUuid).append("&file=").append(URL.encodeQueryString(filename)) .append(verParm).append("&download=").append(download).toString(); } /** * If simply using History.newItem(token), the page won't refresh if current * token equals given token. This method will refresh page (Ajax style * refresh) whatever current token is. * * For sample scenario, delete page, token won't change, but system need go server side * to get "page not found" message by refresh same token(page title). * @param token: * could not be null! */ public static void refreshToken(String token) { History.newItem(token, true); String currToken = History.getToken(); if (currToken != null && currToken.trim().equalsIgnoreCase(token.trim())) { // Only use History.newItem(), it won't refresh page if it has same token, // Test scenarios: delete home page when token is spaceToken(no page title). // After home page delete it will try to redirect to spaceToken, so it is same token, but need refresh page. History.fireCurrentHistoryState(); } } public static native boolean isIE()/*-{ return navigator.appName.indexOf("Microsoft") !=-1; }-*/; public static native boolean isIE6()/*-{ if(navigator.appName.indexOf("Microsoft") !=-1){ var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) if(parseFloat( RegExp.$1 ) == 6) return true; } return false; }-*/; public static boolean isBlack(String color) { if (color == null) return false; if (color.startsWith("#")) color = color.substring(1); char[] c = color.toUpperCase().toCharArray(); for (char d : c) { if (d != '0') return false; } return true; } public static boolean isWhite(String color) { if (color == null) return false; if (color.startsWith("#")) color = color.substring(1); char[] c = color.toUpperCase().toCharArray(); for (char d : c) { if (d != 'F') return false; } return true; } /** * This method should keep consistent with server side DateUtil.toDisplayDate() method * Format long(saved in String format) to display date */ @SuppressWarnings("deprecation") public static String toDisplayDate(long time) { Date date = new Date(time); int year = date.getYear(); int month = date.getMonth() + 1; int day = date.getDate(); int min = date.getMinutes(); Date now = new Date(); int nyear = now.getYear(); int nmonth = now.getMonth() + 1; int nday = now.getDate(); StringBuffer sb = new StringBuffer(); sb.append(date.getHours()).append(":").append((min < 10) ? ("0" + min) : min).append(" "); if (day != nday || month != nmonth || year != nyear) { sb.append(GwtClientUtils.getMonthText(month)); sb.append(" ").append(day); if (year != nyear) { year = year + 1900; sb.append(" ").append(year); } } else { sb.append(" Today"); } return sb.toString(); } /** * @param month * @return */ private static String getMonthText(int month) { if (month == 1) { return Msg.consts.jan(); } else if (month == 2) { return Msg.consts.feb(); } else if (month == 3) { return Msg.consts.mar(); } else if (month == 4) { return Msg.consts.apr(); } else if (month == 5) { return Msg.consts.may(); } else if (month == 6) { return Msg.consts.jun(); } else if (month == 7) { return Msg.consts.jul(); } else if (month == 8) { return Msg.consts.aug(); } else if (month == 9) { return Msg.consts.sep(); } else if (month == 10) { return Msg.consts.oct(); } else if (month == 11) { return Msg.consts.nov(); } return Msg.consts.dec(); } /** * @return */ public static String getToken() { //page#/xxx - xxx must do URLEncode otherwise it will broken URL even its value in anchor part //I found FF looks OK if there is no this decode, but Chrome does work. Anyway, it is safe to decode here and no side-effect. return URL.decodeQueryString(History.getToken()); } /** * @param form * @return */ public static KeyDownHandler createEnterSubmitListener(final FormPanel form) { return new KeyDownHandler() { @Override public void onKeyDown(KeyDownEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { form.submit(); } } }; } public static Widget createUserPortrait(String portraitUrl) { return new HTML(GwtUtils.getUserPortraitHTML(portraitUrl, null, -1)); } public static Widget createUserSmallPortrait(String portraitUrl, String fullname, int size) { return new HTML(GwtUtils.getUserPortraitHTML(portraitUrl, fullname, size)); } public static Widget createUserSmallPortrait(String portraitUrl, int size) { return new HTML(GwtUtils.getUserPortraitHTML(portraitUrl, null, size)); } /** * @param largeLogoUrl * @return */ public static Widget createSpaceLogo(String imgUrl) { return new HTML("<img src=\"" + imgUrl + "\"" + " title=\"logo\">"); } }