openmeetings-db下的util目录

本文深入解读了OpenMeetings项目的util目录下六个Java文件,包括TimezoneUtil处理时区、RoomHelper管理房间活动、LocaleHelper管理地区信息、FormatHelper格式化数据、以及DaoHelper和DtoHelper辅助数据访问与传输。这些工具类在项目中扮演了关键的角色,确保了日期时间处理的准确性及数据操作的效率。
摘要由CSDN通过智能技术生成

2021SC@SDUSC

目录

TimezoneUtil.java

引入OpenmeetingsVariables.getDefaultTimezone

引入java.util.TimeZone

类的定义

RoomHelper.java

LocaleHelper.java

FormatHelper.java

DaoHelper.java和DtoHelper.java

DaoHelper.java

DtoHelper.java

总结


上一篇文章已经展开了对openmeetings-db模块下java源码的分析,对util目录进行了初步探索,完成了util/ws目录的分析,本文将继续分析util目录的其他源码。

TimezoneUtil.java

顾名思义,timezone意为时区,因此TimezoneUtil.java的字面意思应该也是与用户当前的时区相关的一些工具,以下的分析可以按照这个思想展开。

在TimezoneUtil.java源码起始处,首先引入了如下的内容:

 读代码,这应该是引入了某个方法,因此我们先对这个函数的含义进行追溯。

引入OpenmeetingsVariables.getDefaultTimezone

 找到OpenmeetingsVariables.java,它的位置在openmeetings-util模块下,具体位置如下:

由于跨到了另一个模块,因此对OpenmeetingsVariables.java,只需要了解其基本作用。 打开其源码,在起始处,它导入了JSONObject:

 


关于JSONObject,简要作一下说明(参考自博文https://blog.csdn.net/u012448904/article/details/84292821):JSONObject是一种数据结构,可以理解成JSON格式的数据结构,即key-value结构。它可以很方便地转换成字符串,也可以把其他对象转换成JSONObject对象。因此,OpenmeetingsVariables引入JSONObject应该是为了对json数据进行处理。

举例,一个简单的JSONObject对象:{"id":123456,"course":"Math","year":"2021"}


接着向下读源码,定义了OpenmeetingsVariables类:

 其中定义了众多public static final字段,如ATTR_CLASS、PARAM_SRC等等,还有private static字段,如webAppRootKey、JSONObject(用到了引入的JSONObject)、defaultTimezone等等,接下来就是一些方法,包括在TimezoneUtil.java文件中引入的getDefaultTimezone方法。

defaultTimezone字段的定义如下:

private static String defaultTimezone = "Europe/Berlin";

getDefaultTimezone方法的定义如下:

public static String getDefaultTimezone() {
		return defaultTimezone;
	}

也就是说,defaultTimezone表示默认时区,getDefaultTimezone是其getter。在TimezoneUtil.java中引入这个方法,利用了openmeetings-util模块的内容,能够获取当前的默认时区信息。

引入java.util.TimeZone

回到TimezoneUtil.java,继续读源码。接着,又引入了java.util.TimeZone:

既然是java.util.*,TimeZone应该是java提供的工具类,而不是openmeetings项目所提供。在字面意思上看,也是时区。

查阅资料(参考自博文https://blog.csdn.net/weixin_42485891/article/details/114050915
)可知,TimeZone表示的是时区偏移量,也可以计算夏令时。在操作Date、Calendar等表示日期/时间的对象时,经常会用到TimeZone,因为不同的时区里时间不同。

其创建方式举例:

1)获取默认的TimeZone对象:

TimeZone tz=TimeZone.getDefault();

2)使用getTimeZone(String id)方法获取。例如:

TimeZone china=TimeZone.getTimeZone("GMT+:08:00");

 总的来说,TimeZone在TimezoneUtil.java中应该同样确定时区,在日期、时间等信息中发挥关键作用。

类的定义

继续读源码,还引入了两个类:

第一个是同样在db目录下的User类,此处还没有分析,先留个坑;第二个是apache.wicket.util.string下的Strings,是对字符串进行一系列处理的类。

接下来就是TimezoneUtil类的定义了:

 类的定义比较简单,只有一个构造器和两个public static方法。

第一个方法是public static TimeZone getTimeZone(String timeZoneId)。如同前面介绍TimeZone类时的介绍一样,这里也是根据传入的id来获取时区对象。如果传入的id为空,则调用引入的getDefaultzone函数,通过其默认信息,找到默认时区对象并返回,否则就根据传入的id获取时区对象。

第二个方法则是第一个方法的重载,传入的参数是User对象。如果User对象为空,则调用自身即getTimeZone函数,传入的参数为null,也就是第一个方法中id为空的情况。如果User对象不为空,则同样调用自身,传入的参数为User对象.getTimeZoneId(),具体原理先留个坑。

至此,TimeZoneUtil.java分析完毕。根据分析,它主要起到了获取当前时区的作用,分为通过timeZoneId和User对象来获取。 

附上TimeZoneUtil.java的源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultTimezone;

import java.util.TimeZone;

import org.apache.openmeetings.db.entity.user.User;
import org.apache.wicket.util.string.Strings;

public class TimezoneUtil {
	private TimezoneUtil() {}
	/**
	 *
	 * @param timeZoneId
	 *            the ID for a TimeZone, either an abbreviation such as "PST", a
	 *            full name such as "America/Los_Angeles", or a custom ID such as
	 *            "GMT-8:00". Note that the support of abbreviations is for JDK
	 *            1.1.x compatibility only and full names should be used.
	 * @return the specified TimeZone, or the GMT zone if the given ID cannot be
	 *         understood.
	 */
	public static TimeZone getTimeZone(String timeZoneId) {
		return TimeZone.getTimeZone(Strings.isEmpty(timeZoneId) ? getDefaultTimezone() : timeZoneId);
	}

	/**
	 * Returns the timezone based on the user profile, if not return the timezone
	 * from the server
	 *
	 * @param user
	 *            to get timezone for
	 * @return {@link TimeZone} of given user
	 */
	public static TimeZone getTimeZone(User user) {
		return getTimeZone(user == null ? null : user.getTimeZoneId());
	}
}

RoomHelper.java

在同样的目录下,继续分析RoomHelper.java。字面意思,它是对房间提供一定的帮助,也就是与会话中的房间相关。使用IDEA打开源码,首先看其引入的类、方法等:

前面三个引入的都是db目录下的类,同样先留个坑;后面两个引入的分别是JSONArray和JSONObject,其中JSONObject在前文已经解释过了。


关于JSONArray,作一下简单的阐述(参考自博文https://m.php.cn/article/419391.html
):

JSONArray是一个有序的值序列,它的外部文本形式是一个用方括号括起来的字符串,用逗号分隔值,内部表单是具有索引的对象“get”和“opt”用于通过索引访问值的“element”方法,以及用于添加或替换值的方法。它的值可以是任何这些类型的:Boolean,JSONArray,JSONObject,Number,String,或JSONNul object。

个人的通俗理解:JSONArray就是JSONObject构成的数组。例如:[{"id":123456,"course":"math","year":"2021"},{"name":"Tom","age":18}]


接下来开始阅读类的定义,其大致情况如下:

可以清楚地看到,类内一共定义了一个构造器和两个public static方法。构造器没有什么好说的,而第一个方法体内又调用了第二个方法,所以先看第二个public static方法:

 addScreenActivities方法,含义是添加屏幕活动,参数列表为JSONObject o和StreamClient sc(在开头引入的类)。

方法体内,先new一个JSONArray a。然后进行一个判断:如果sc的type是sharing(猜测其含义:客户信息流sc是正在分享的状态),则依次判断sc的三个特性,并在a中添加相应的字符串元素。最后,在o中put一个新的键值对,key为"screenActivities",value为a,并返回。

这个流程走下来,就完成了添加屏幕活动的目标。

现在回头看第一个方法:

videoJson方法,含义是用json格式表示一个视频的信息,它的返回值是一个JSONObject对象,传入的参数有Client c,是一个Client对象,先暂定为一个客户;boolean self,猜测应该表示是否是当前用户,以区分房间中的角色;String sid,猜测应该是服务的id标识;IStreamClientManager mgr,应该是管理客户的视频信息产生的流;String uid,应该是用户的id标识。

在方法体内,首先创立了一个StreamClient对象sc,通过mgr.get(uid)获取。其含义,应该是在mgr信息流中,得到uid代表的用户的那部分信息。如果sc为空,则new一个JSONObject返回。否则,通过将c进行json化来定义JSONObject o,并且添加若干字段,包括了sid、uid等等,最终调用addScreenActivities(o,sc)。调用addScreenActivities的过程,在o中添加了由sc的信息生成的新的屏幕活动。

总结来看,RoomHelper.java完成的工作就是根据用户的若干信息得到表示视频的json数据,并可以通过这个json数据在屏幕中添加一个活动,例如屏幕共享等。

附上RoomHelper.java的源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import org.apache.openmeetings.db.entity.basic.Client;
import org.apache.openmeetings.db.entity.room.StreamClient;
import org.apache.openmeetings.db.manager.IStreamClientManager;

import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;

public class RoomHelper {
	private RoomHelper() {}

	public static JSONObject videoJson(Client c, boolean self, String sid, IStreamClientManager mgr, String uid) {
		StreamClient sc = mgr.get(uid);
		if (sc == null) {
			return new JSONObject();
		}
		JSONObject o = c.toJson(self)
				.put("sid", sid)
				.put("uid", sc.getUid())
				.put("broadcastId", sc.getBroadcastId())
				.put("width", sc.getWidth())
				.put("height", sc.getHeight())
				.put("type", sc.getType());
		return addScreenActivities(o, sc);
	}

	public static JSONObject addScreenActivities(JSONObject o, StreamClient sc) {
		JSONArray a = new JSONArray();
		if (Client.Type.sharing == sc.getType()) {
			if (sc.isSharingStarted()) {
				a.put("sharing");
			}
			if (sc.isRecordingStarted()) {
				a.put("recording");
			}
			if (sc.isPublishStarted()) {
				a.put("publish");
			}
		}
		return o.put("screenActivities", a);
	}
}

LocaleHelper.java

上一篇文章提到,Locale类的作用是转换和划分地区的国际化类。因此,LocaleHelper.java的作用应该就是对地区进行控制与利用。

首先仍然先看其导入的其他类:

最开始引入了OpenmeetingsVariables下面的方法getWebAppRootKey,与之前分析的getDefaultTimezone类似,是webAppRootKey字段的getter。然后是引入了java提供的Arrays、HashSet、List、Locale、Set这几个类,基本用法都可以很容易查到,不再赘述。

接下来, 引入了db模块下的两个类,同样先留个坑。

然后引入了org.red5.logging.Red5LoggerFactory。最初对openmeetings项目概述的时候提到,openmeetings是基于red5流媒体服务器的在线会话工具。这里的Red5LoggerFactory就是red5所定义的类,它简化了获取日志实例的请求操作,在Red5应用程序中推荐使用。简单来说,它是用来处理red5流媒体服务器中日志、记录相关的问题。

最后引入的是org.slf4j.Logger。参考博文(https://www.cnblogs.com/liyujava/p/10185555.html
)后得知,java开发过程中经常需要打印日志信息,往往会在每个类的第一行加上形如以下的代码:protected static final Logger logger=LoggerFactory.getLogger(xx.class)。其目的是使用指定的类初始化日志对象,方便在日志输出的时候可以打印出日志信息所属的类。输出的方法为logger.debug("hello"),打印的时候格式为"xx:hello"。

弄清楚了引入的类,接下来看类的定义。

果不其然,第一行代码就初始化了Logger对象!初始化的参数,除了通过LocaleHelper.class之外,还有getWebAppRootKey()。它会在打印日志信息的时候一起输出,确定webApp的标识。

接下来是构造器,没有什么好说的。紧接着是一个public static方法getCountries(),返回值类型为List<String>。阅读其方法体,在其中调用了Arrays.asList,它可以将参数数组转化成一个List。而它的参数,是调用了Locale类的静态方法getISOCountries()。Locale类的静态方法getISOCountries(),它的作用是返回ISO3166中存在的所有两个字母国家/地区代码的String数组。也就是说,getISOCountries返回国家/地区代码数组,它们会被转化成List并返回。猜想,getCountries的作用就是获取所有符合要求的国家/地区代码,供后面的其他方法使用。

下一个public static方法是getCountryName,参数为String code和Locale l。方法体内,创建了一个新的Locale对象,并对它进行一系列的操作并返回,这些操作比较复杂。首先是Locale.Builder():在Locale类的定义中,有一个内部类Builder,它的作用就是通过setter传入的参数构建Locale实例。在这里的Locale.Builder()是一个构造器,构造了一个Builder对象,然后setRegion(code),即将region设置为code,再进行build,构建一个Locale对象。构建成功之后,调用getDisplayCountry(),获取当前Locale对象给定语言环境的国家或地区的名称。因此,简单来说,getCountryName方法就是获取当前用户所在地的名称。

接下来的方法也是getCountryName,与上面构成重载,原理相同,不再赘述。

然后是public static方法validateCountry(String _code)。根据前端的经验,validate主要用于验证。初步猜想,这个方法也是这个目的。其完整代码如下:

方法体内,首先调用getCountries()获取所有合法的国家代码,存放在变量list中。再新建一个Set对象countries,存放的数据类型为String,并通过list将它初始化为一个HashSet,便于直接检验代码是否存在。

接下来,新建一个String变量code。判断_code是不是null,如果为null,则将code赋值为空串;否则,调用toUpperCase方法,将_code转为大写。调用toUpperCase方法时参数列表传入了一个Locale对象,是Locale.ROOT。参数中传入Locale对象,是要针对该区域确定转换规则。而这个Locale.ROOT,是由两个空字符串初始化的对象,表示所有语言环境的基本语言环境,并且用于语言/国家无关的区域设置,用于区域设置敏感的操作。也就是说,这里的转换规则没有特殊语种的要求。

转换完成后,如果在countries中不存在值为code的key,说明这个code是非法的。因此,通过list.get(0),取了list中第一个代码,将它赋给code,并打印提示。方法的最后,将code返回。

所以,validateCountry方法的作用就是对传入的代码进行处理并检验是否合法,如果合法就直接返回,如果不合法就默认返回ISO代码列表中第一个代码。

然后,接下来的两个getLocale方法就简单得多,基本就是通过传入的语言标识langId,判断国家/地区,并返回Locale对象;或者根据传入的User对象,经过一系列操作获得Locale对象。逻辑比较简单,也不再多说。

至此,LocaleHelper.java分析结束。在这个java文件里,主要完成了对用户所处的国家/地区的获取、判断等操作。

附上LocaleHelper.java的源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.openmeetings.db.dao.label.LabelDao;
import org.apache.openmeetings.db.entity.user.User;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;

public class LocaleHelper {
	private static final Logger log = Red5LoggerFactory.getLogger(LocaleHelper.class, getWebAppRootKey());

	private LocaleHelper() {}

	public static List<String> getCountries() {
		return Arrays.asList(Locale.getISOCountries());
	}

	public static String getCountryName(String code, Locale l) {
		return new Locale.Builder().setRegion(code).build().getDisplayCountry(l);
	}

	public static String getCountryName(String code) {
		return new Locale.Builder().setRegion(code).build().getDisplayCountry();
	}

	public static String validateCountry(String _code) {
		List<String> list = getCountries();
		Set<String> countries = new HashSet<>(list);
		String code = _code == null ? "" : _code.toUpperCase(Locale.ROOT);
		if (!countries.contains(code)) {
			String newCountry = list.get(0);
			log.warn("Invalid country found: {}, will be replaced with: {}", code, newCountry);
			code = newCountry;
		}
		return code;
	}

	public static Locale getLocale(Long langId) {
		return langId == 3 ? Locale.GERMANY : LabelDao.getLocale(langId);
	}

	public static Locale getLocale(User u) {
		Locale locale = getLocale(u.getLanguageId());
		try {
			Locale.Builder builder = new Locale.Builder().setLanguage(locale.getLanguage());
			if (u.getAddress() != null && u.getAddress().getCountry() != null) {
				builder.setRegion(u.getAddress().getCountry());
			}
			locale = builder.build();
		} catch (Exception e) {
			log.error("Unexpected Error while constructing locale for the user", e.getMessage());
		}
		return locale;
	}
}

FormatHelper.java

接下来继续看FormatHelper.java。它的含义大概是格式、编排,亦或是规划,等等,提供的类和方法应该是基于这些方面的工具。

首先看它导入的类和方法:

第一个导入的是java的DateFormat类下面的一个常量SHORT。关于DateFormat(参考自博文https://blog.csdn.net/a1439775520/article/details/98444189),它的作用是格式化并解析日期/时间。也就是说,它是Date的格式化工具,能够帮助我们格式化Date,进而将Date转换成我们想要的String字符串供我们使用。它支持格式化风格包括FULL、LONG、MEDIUM和SHORT4种。


简单举个例子:

1、DateFormat.SHORT

完全为数字,如12,.13.52或3:30pm

2、DateFormat.MEDIUM

较长,如Jan 12,1952

3、DateFormat.LONG

更长,如January 12,1952或3:30:32pm

4、DateFormat.FULL

完全指定,如Tuesday、April 12、1952AD或3:30:42pm PST


第二个引入就是java提供的StringEscapeUtils使用类下的escapeHtml4方法。它比较复杂,个人理解来说,就是对前端提交到后台的数据进行过滤,防止恶意用户对html的特殊攻击。

第三个引入的就是前文分析过的TimezoneUtil下的getTimeZone方法了,用来获取时区信息。

第四个引入的,是java.util.regex.Pattern类,即正则表达式类,负责对字符串进行正则表达式的处理。以下内容参考自博文(https://www.cnblogs.com/zhaochunhua/p/3569626.html):正则表达式通常以字符串的形式出现,它首先必须被编译成Pattern类的一个实例。结果模型可以用来生成一个Matcher,它可以匹配根据这个正则表达式生成的任意字符序列。在实现一个匹配器中的匹配时包括了任意多的情况,并且多个匹配器可以共享同一个匹配模式。

举例:

Pattern p=Pattern.compile("a*b");
Matcher m=p.matcher("aaaaab");
boolean b=m.matcher();

为了方便使用,Pattern类也定义了matches方法,例如boolean b=Pattern.matches("a*b","aaaaab");就等价于以上三条语句。

第五个引入的是apache提供的FastDateFormat类。对于这个类,网络上的资料较少,暂且认为它起的作用是能够更好保证线程安全的String类型转换。

最后两个都是前面提及的,一个是还没有分析到的User,另一个是对字符串处理的Strings。

下面开始看类的定义,基本结构如下:

首先定义了一个private static final字段,类型为Pattern,定义的格式与刚才的举例相同,主要起到一个字符串模式匹配器的作用,不再多说。后面的构造器也省略。

接着,是一个public static方法isRtLanguage,传入的参数是表示语言的字符串。方法体内来判断这个字符串是否合法,实现的思路就是用刚才定义的匹配器去匹配。

再下面,是两个重载的formatUser方法。由于第一个方法调用了第二个方法,因此我们先看第二个:

返回值是String,传入的参数是User u和boolean isHTMLEscape(判断是否应该对user信息进行过滤)。方法体内,首先初始化一个String对象user为空串。接着,对u进行判断,如果u不为空,则再对u进行一系列操作。

这些操作的流程是:先获取u的信息中的email(u.getAddress().getEmail()),并保存为字符串email。再判断,如果u的firstname和lastname都为空,则user字符串直接赋值为email;否则,其firstname、lastname以及email进行字符串的格式化,并赋值给user。判断结束后,再根据isHTMLEscape决定是否要对user进行过滤。

操作结束后,返回user。

这个方法的作用,应该还是对User对象进行标准格式化,使其能够以一种标准、统一的字符串形式表示出来。

再回头看第一个formatUser方法。它直接调用了第二个重载,并将isHTMLEscape设置为false,即不需要对它进行过滤。这也就告诉我们,默认情况下不需要对u进行过滤,所以调用第一个,如果需要才调用第二个。

最后的三个方法,都是获取格式化的日期、时间。其获取的方式是通过用户的时区Timezone、地点Locale,获取的格式都是SHORT。比较简单,不再多说。

至此,FormatHelper.java分析结束。它的主要目的是对用户、日期和时间等进行一系列标准化、格式化,在应用中更加统一高效。

附上FormatHelper.java的源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import static java.text.DateFormat.SHORT;
import static org.apache.commons.text.StringEscapeUtils.escapeHtml4;
import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;

import java.util.regex.Pattern;

import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.wicket.util.string.Strings;

public class FormatHelper {
	/**
	 * taken from BidiUtils
	 *
	 * A regular expression for matching right-to-left language codes. See
	 * {@link #isRtlLanguage} for the design.
	 */
	private static final Pattern RtlLocalesRe = Pattern.compile("^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))"
					+ "(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)");

	private FormatHelper() {}

	/**
	 * Check if a BCP 47 / III language code indicates an RTL language, i.e.
	 * either: - a language code explicitly specifying one of the right-to-left
	 * scripts, e.g. "az-Arab", or
	 * <p>
	 * - a language code specifying one of the languages normally written in a
	 * right-to-left script, e.g. "fa" (Farsi), except ones explicitly
	 * specifying Latin or Cyrillic script (which are the usual LTR
	 * alternatives).
	 * <p>
	 * The list of right-to-left scripts appears in the 100-199 range in
	 * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
	 * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko,
	 * and Tifinagh, which also have significant modern usage. The rest (Syriac,
	 * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern
	 * usage and are not recognized. The languages usually written in a
	 * right-to-left script are taken as those with Suppress-Script:
	 * Hebr|Arab|Thaa|Nkoo|Tfng in
	 * http://www.iana.org/assignments/language-subtag-registry, as well as
	 * Sindhi (sd) and Uyghur (ug). The presence of other subtags of the
	 * language code, e.g. regions like EG (Egypt), is ignored.
	 *
	 * @param languageString - locale string
	 * @return <code>true</code> in case passed locale is right-to-left
	 */
	public static boolean isRtlLanguage(String languageString) {
		return languageString != null && RtlLocalesRe.matcher(languageString).find();
	}

	public static String formatUser(User u) {
		return formatUser(u, false);
	}

	public static String formatUser(User u, boolean isHTMLEscape) {
		String user = "";
		if (u != null) {
			String email = u.getAddress() == null ? "" : u.getAddress().getEmail();
			if (Strings.isEmpty(u.getFirstname()) && Strings.isEmpty(u.getLastname())) {
				user = email;
			} else {
				user = String.format("\"%s %s\" <%s>", u.getFirstname(), u.getLastname(), email);
			}
			user = isHTMLEscape ? escapeHtml4(user) : user;
		}
		return user;
	}

	public static FastDateFormat getDateFormat(User u) {
		return FastDateFormat.getDateInstance(SHORT, getTimeZone(u), LocaleHelper.getLocale(u));
	}

	public static FastDateFormat getTimeFormat(User u) {
		return FastDateFormat.getTimeInstance(SHORT, getTimeZone(u), LocaleHelper.getLocale(u));
	}

	public static FastDateFormat getDateTimeFormat(User u) {
		return FastDateFormat.getDateTimeInstance(SHORT, SHORT, getTimeZone(u), LocaleHelper.getLocale(u));
	}
}

DaoHelper.java和DtoHelper.java

接下来要分析的是DaoHelper.java和DtoHelper.java。dao和dto,前面说过,分别是数据访问对象和数据传输对象。因此在这里提到两个helper,应该也是对数据访问和传输提供工具。

DaoHelper.java

首先来看DaoHelper.java。先看它引入的内容:

后面两个都是与字符串处理相关,所以只看第一个就可以。第一个是java提供的TypedQuery类,按照查到的资料显示,它是与数据库操作相关。由于资料较少,所以先暂时这么认为,如果遇到新的问题再去查证。

接下来就是类的定义了,其基本结构如下:

内容比较多,也比较复杂,需要一条条看。

首先,DaoHelper也定义了public static final变量,数据类型是UnsupportOperationException,变量名为UNSUPPORTED。这是一个异常,在后面的方法中会抛出。下面的构造器也不再说,后面紧接着就是一些public static方法。前面的4个方法是getSearchQuery方法重载,返回值均为String,形参列表不同。它们的作用应该都是对数据库进行某种操作,而前三个重载都调用了第四个重载,因此我们先来看第四个重载。

形参列表是String table,String alias,String join,String search,boolean distinct,boolean filterDeleted,boolean count,String additionalWhere,String sort,String... fields(“...”表示可变长度参数,可以接收0到多个该类型的对象)。

再看方法体,具体代码如下:

public static String getSearchQuery(String table, String alias, String join, String search, boolean distinct, boolean filterDeleted, boolean count, String additionalWhere, String sort, String... fields) {
		StringBuilder sb = new StringBuilder("SELECT ");
		if (count) {
			sb.append("COUNT(");
		}
		if (distinct) {
			sb.append("DISTINCT ");
		}
		sb.append(alias);
		if (count) {
			sb.append(")");
		}
		sb.append(" FROM ").append(table).append(" ").append(alias);
		if (!Strings.isEmpty(join)) {
			sb.append(" ").append(join);
		}
		sb.append(" WHERE 1 = 1 ");
		if (filterDeleted) {
			sb.append("AND ").append(alias).append(".deleted = false ");
		}
		appendWhereClause(sb, search, alias, fields);
		if (!Strings.isEmpty(additionalWhere)) {
			sb.append("AND ").append(additionalWhere);
		}
		return appendSort(sb, alias, sort).toString();
	}

首先new了一个StringBuilder,以“SELECT ”开头。接着判断,如果count为true,意味着是要查询某类元组的数目,则拼接上"COUNT(";如果distinct为true,意味着查询的时候排除重复值,则拼接上"DISTINCT "。接着,拼接上要查询的属性或者字段,即alias。如果刚才添加了"COUNT(",此时就要添加")"。然后以此类推,整体完成一个sql语句。 

值得注意的是,在完成这个方法的过程中,还调用了后面的方法,如appendWhereClause()等。这些方法其中的逻辑都比较简单,对字符串进行若干容易的处理,所以不再多说。前面三个重载,也是在某些实参省略的情况下默认给它传入一些参数,与前文讲过的情况相同,不再赘述。

至此,DaoHelper.java就分析结束了。这部分的内容看上去比较多,但核心就是对数据库的查询进行处理,通过传入的变量完成一个sql语句,供其他地方使用,从而完成数据的查询。

附上DaoHelper.java的源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import javax.persistence.TypedQuery;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.util.string.Strings;

public class DaoHelper {
	public static final UnsupportedOperationException UNSUPPORTED = new UnsupportedOperationException("Should not be used");

	private DaoHelper() {}

	public static String getSearchQuery(String table, String alias, String search, boolean filterDeleted, boolean count, String sort, String... fields) {
		return getSearchQuery(table, alias, search, false, filterDeleted, count, sort, fields);
	}

	public static String getSearchQuery(String table, String alias, String search, boolean distinct, boolean filterDeleted, boolean count, String sort, String... fields) {
		return getSearchQuery(table, alias, null, search, distinct, filterDeleted, count, null, sort, fields);
	}

	public static String getSearchQuery(String table, String alias, String join, String search, boolean filterDeleted, boolean count, String additionalWhere, String sort, String... fields) {
		return getSearchQuery(table, alias, join, search, false, filterDeleted, count, additionalWhere, sort, fields);
	}

	public static String getSearchQuery(String table, String alias, String join, String search, boolean distinct, boolean filterDeleted, boolean count, String additionalWhere, String sort, String... fields) {
		StringBuilder sb = new StringBuilder("SELECT ");
		if (count) {
			sb.append("COUNT(");
		}
		if (distinct) {
			sb.append("DISTINCT ");
		}
		sb.append(alias);
		if (count) {
			sb.append(")");
		}
		sb.append(" FROM ").append(table).append(" ").append(alias);
		if (!Strings.isEmpty(join)) {
			sb.append(" ").append(join);
		}
		sb.append(" WHERE 1 = 1 ");
		if (filterDeleted) {
			sb.append("AND ").append(alias).append(".deleted = false ");
		}
		appendWhereClause(sb, search, alias, fields);
		if (!Strings.isEmpty(additionalWhere)) {
			sb.append("AND ").append(additionalWhere);
		}
		return appendSort(sb, alias, sort).toString();
	}

	public static StringBuilder appendWhereClause(StringBuilder _sb, String search, String alias, String... fields) {
		if (!Strings.isEmpty(search) && fields != null) {
			boolean notEmpty = false;
			StringBuilder sb = new StringBuilder();
			String[] searchItems = search.replace("\'", "").replace("\"", "").split(" ");
			for (int i = 0; i < searchItems.length; ++i) {
				if (searchItems[i].isEmpty()) {
					continue;
				}
				if (i == 0) {
					notEmpty = true;
					sb.append(" (");
				} else {
					sb.append(" OR ");
				}
				StringBuilder placeholder = new StringBuilder();
				placeholder.append("%").append(StringUtils.lowerCase(searchItems[i])).append("%");

				sb.append("(");
				for (int j = 0; j < fields.length; ++j) {
					if (j != 0) {
						sb.append(" OR ");
					}
					sb.append("lower(").append(alias).append(".").append(fields[j]).append(") LIKE '").append(placeholder).append("' ");
				}
				sb.append(")");
			}
			if (notEmpty) {
				sb.append(") ");
				_sb.append(" AND").append(sb);
			}
		}
		return _sb;
	}

	public static StringBuilder appendSort(StringBuilder sb, String alias, String sort) {
		if (!Strings.isEmpty(sort)) {
			sb.append(" ORDER BY ").append(alias).append(".").append(sort);
		}
		return sb;
	}

	public static String getStringParam(String param) {
		return param == null ? "%" : "%" + StringUtils.lowerCase(param) + "%";
	}

	public static <T> TypedQuery<T> setLimits(TypedQuery<T> q, Long first, Long max) {
		if (first != null) {
			q.setFirstResult(first.intValue());
		}
		if (max != null) {
			q.setMaxResults(max.intValue());
		}
		return q;
	}
}

DtoHelper.java

与DaoHelper.java相比,DtoHelper.java就简单一些了。首先来看引入的部分:

导入的内容比较清晰,除了java提供的ArrayList和Collection类之外,就是前面已经接触过的JSONObject和JSONArray。因此直接看类的定义:

第一行是构造器。

接下来两个方法类似。optInt方法,传入了JSONObject o和String key,如果o有属性key并且值非空,则返回这个值,否则返回null。这个过程中,key对应的值应该是int类型,而下一个方法应该是long类型。

最后两个方法的代码如下:

public static <T extends Enum<T>> T optEnum(Class<T> clazz, JSONObject o, String key) {
		return o.has(key) && !o.isNull(key) ? Enum.valueOf(clazz, o.getString(key)) : null;
	}

	public static <T extends Enum<T>> Collection<T> optEnumList(Class<T> clazz, JSONArray arr) {
		Collection<T> l = new ArrayList<>();
		if (arr !=  null) {
			for (int i = 0; i < arr.length(); ++i) {
				l.add(Enum.valueOf(clazz, arr.getString(i)));
			}
		}
		return l;
	}

这里需要解释一下Enum的valueOf方法。网络的解释比较杂乱,我个人理解,它会将给定的枚举类型中与指定的String值相同的值输出(暂时看上去没有什么用)。这两个方法都利用了valueOf方法,基本逻辑也比较简单,不再多说。

至此,DtoHelper.java分析完毕。附上源码:

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.db.util;

import java.util.ArrayList;
import java.util.Collection;

import com.github.openjson.JSONArray;
import com.github.openjson.JSONObject;

public class DtoHelper {
	private DtoHelper() {}

	public static Integer optInt(JSONObject o, String key) {
		return o.has(key) && !o.isNull(key) ? o.getInt(key) : null;
	}

	public static Long optLong(JSONObject o, String key) {
		return o.has(key) && !o.isNull(key) ? o.getLong(key) : null;
	}

	public static <T extends Enum<T>> T optEnum(Class<T> clazz, JSONObject o, String key) {
		return o.has(key) && !o.isNull(key) ? Enum.valueOf(clazz, o.getString(key)) : null;
	}

	public static <T extends Enum<T>> Collection<T> optEnumList(Class<T> clazz, JSONArray arr) {
		Collection<T> l = new ArrayList<>();
		if (arr !=  null) {
			for (int i = 0; i < arr.length(); ++i) {
				l.add(Enum.valueOf(clazz, arr.getString(i)));
			}
		}
		return l;
	}
}

总结

本文分析了util目录下的6个java文件,分别讨论了其源码以及作用,也完成了该目录下大部分源码的研究。由于这个目录的主要目的是工具,因此没有看到太多定义类的应用,主要都是类的定义,逻辑上并无太复杂的部分。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值