Java tutorial
package models; import static org.apache.commons.lang.StringUtils.isNotEmpty; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OrderBy; import javax.persistence.Query; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import models.Module.QueryParams; import models.Module.QueryParams.Suffix; import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; import play.db.jpa.JPA; import play.db.jpa.Model; import util.CeylonElementType; import util.Util; import controllers.RepoAPI; @Entity @SuppressWarnings("serial") @Table(uniqueConstraints = @UniqueConstraint(columnNames = { "module_id", "version" })) public class ModuleVersion extends Model implements Comparable<ModuleVersion> { @Column(nullable = false) public String version; @JoinColumn(nullable = false) @ManyToOne public Module module; @Column(nullable = false) public Date published; public boolean isCarPresent; public boolean isJarPresent; public boolean isDocPresent; public boolean isJsPresent; public boolean isAPIPresent; public boolean isSourcePresent; public boolean isScriptsPresent; public boolean isResourcesPresent; public boolean isRunnable; public long downloads; public long jsdownloads; public long sourceDownloads; @Column(columnDefinition = "TEXT") // Hibernate would map @Lob to a CLOB instead of TEXT public String doc; @Column(columnDefinition = "TEXT") // Hibernate would map @Lob to a CLOB instead of TEXT public String license; @Column(columnDefinition = "TEXT") // Hibernate would map @Lob to a CLOB instead of TEXT public String changelog; public int jvmBinMajor; public int jvmBinMinor; public int jsBinMajor; public int jsBinMinor; @OrderBy("name,version") @OneToMany(mappedBy = "moduleVersion", cascade = CascadeType.REMOVE) public List<Dependency> dependencies = new ArrayList<Dependency>(); @Sort(comparator = ModuleMemberComparator.class, type = SortType.COMPARATOR) @OrderBy("packageName,name") @OneToMany(mappedBy = "moduleVersion", cascade = CascadeType.REMOVE) private SortedSet<ModuleMember> members = new TreeSet<ModuleMember>(); @Sort(comparator = ModuleScriptComparator.class, type = SortType.COMPARATOR) @OrderBy("name,plugin,unix") @OneToMany(mappedBy = "moduleVersion", cascade = CascadeType.REMOVE) private SortedSet<ModuleScript> scripts = new TreeSet<ModuleScript>(); @OrderBy("name") @ManyToMany public List<Author> authors = new ArrayList<Author>(); @Transient public String getPath() { return module.name.replace('.', '/') + "/" + version; } @Transient public String getAPIPath() { return getPath() + "/module-doc/api/index.html"; } @Transient public String getCarPath() { return getPath() + "/" + module.name + "-" + version + ".car"; } @Transient public String getJarPath() { return getPath() + "/" + module.name + "-" + version + ".jar"; } @Transient public String getJsPath() { return getPath() + "/" + module.name + "-" + version + ".js"; } @Transient public String getSourcePath() { return getPath() + "/" + module.name + "-" + version + ".src"; } @Transient public String getScriptsPath() { return getPath() + "/" + module.name + "-" + version + ".scripts.zip"; } @Transient public String getDocPath() { return getPath() + "/" + "module-doc.zip"; } public void addDependency(String name, String version, boolean optional, boolean export, boolean resolvedFromMaven) { Dependency dep = new Dependency(this, name, version, optional, export, resolvedFromMaven); dep.create(); dependencies.add(dep); } public void addMember(String packageName, String name, CeylonElementType type) { ModuleMember member = new ModuleMember(this, packageName, name, type); member.create(); members.add(member); } public void addScript(String name, String description, boolean unix, boolean plugin, String pluginModule) { ModuleScript script = new ModuleScript(this, name, description, unix, plugin, pluginModule); script.create(); scripts.add(script); } public int getDependentModuleVersionCount() { return JPA.em().createQuery( "SELECT count(v) FROM ModuleVersion v JOIN v.dependencies d WHERE d.name=:name and d.version=:version", Long.class).setParameter("name", module.name).setParameter("version", version).getSingleResult() .intValue(); } public List<ModuleVersion> getDependentModuleVersions() { return JPA.em().createQuery( "SELECT v FROM ModuleVersion v JOIN v.dependencies d WHERE d.name=:name and d.version=:version", ModuleVersion.class).setParameter("name", module.name).setParameter("version", version) .getResultList(); } @Override public int compareTo(ModuleVersion other) { return Util.compareVersions(version, other.version); } // // Static helpers public static SortedMap<String, SortedSet<ModuleVersion>> findDependants(String moduleName, String version) { String query = "SELECT d.version, v FROM ModuleVersion v JOIN v.dependencies d LEFT JOIN FETCH v.module WHERE d.name=:name"; if (version != null && !version.isEmpty()) { query += " AND d.version=:version"; } Query jpa = JPA.em().createQuery(query).setParameter("name", moduleName); if (version != null && !version.isEmpty()) { jpa.setParameter("version", version); } List<Object[]> results = jpa.getResultList(); Comparator<String> versionComparator = new Comparator<String>() { @Override public int compare(String v1, String v2) { return Util.compareVersions(v1, v2); } }; Comparator<ModuleVersion> dependantComparator = new Comparator<ModuleVersion>() { @Override public int compare(ModuleVersion v1, ModuleVersion v2) { int result = v1.module.name.compareTo(v2.module.name); if (result == 0) { result = Util.compareVersions(v1.version, v2.version); } return result; } }; SortedMap<String, SortedSet<ModuleVersion>> dependantsMap = new TreeMap<String, SortedSet<ModuleVersion>>( versionComparator); for (Object[] result : results) { String ver = (String) result[0]; ModuleVersion dependant = (ModuleVersion) result[1]; SortedSet<ModuleVersion> dependants = dependantsMap.get(ver); if (dependants == null) { dependants = new TreeSet<ModuleVersion>(dependantComparator); dependantsMap.put(ver, dependants); } dependants.add(dependant); } return dependantsMap; } public static long findDependantsCount(String moduleName) { return JPA.em().createQuery("SELECT count(v) FROM ModuleVersion v JOIN v.dependencies d WHERE d.name=:name", Long.class).setParameter("name", moduleName).getSingleResult().longValue(); } public static ModuleVersion findByVersion(String name, String version) { return find("module.name = ? AND version = ?", name, version).first(); } public static List<ModuleVersion> findByModule(Module module) { return find("module = ? ORDER BY published DESC", module).fetch(); } public static void incrementDownloads(ModuleVersion v) { em().createNativeQuery("UPDATE ModuleVersion set downloads = downloads + 1 WHERE id = ?") .setParameter(1, v.id).executeUpdate(); } public static void incrementJSDownloads(ModuleVersion v) { em().createNativeQuery("UPDATE ModuleVersion set jsdownloads = jsdownloads + 1 WHERE id = ?") .setParameter(1, v.id).executeUpdate(); } public static void incrementSourceDownloads(ModuleVersion v) { em().createNativeQuery("UPDATE ModuleVersion set sourceDownloads = sourceDownloads + 1 WHERE id = ?") .setParameter(1, v.id).executeUpdate(); } public static List<ModuleVersion> latest(int max) { return find("ORDER BY published DESC").fetch(max); } public static List<ModuleVersion> latestForModule(String module, int max) { return find("module.name = ? ORDER BY published DESC", module).fetch(max); } public static List<ModuleVersion> latestForOwner(User owner, int max) { return find("module.owner = ? ORDER BY published DESC", owner).fetch(max); } public static long countForOwner(User owner) { return count("module.owner = ?", owner); } static String getBackendQuery(String prefix, QueryParams type) { StringBuilder query = new StringBuilder(); boolean first = true; for (Suffix suffix : type.getSuffixes()) { if (!first) { switch (type.getRetrieval()) { case ANY: query.append(" OR "); break; case ALL: query.append(" AND "); break; default: // ouch throw new RuntimeException( "Invalid switch statement: missing enum cases " + type.getRetrieval()); } } String q; switch (suffix) { case CAR: q = prefix + "isCarPresent = true"; break; case JAR: q = prefix + "isJarPresent = true"; break; case JS: q = prefix + "isJsPresent = true"; break; case SRC: q = prefix + "isSourcePresent = true"; break; case RESOURCES: q = prefix + "isSourcePresent = true"; break; case DOCS: q = prefix + "isSourcePresent = true"; break; case SCRIPTS_ZIPPED: q = prefix + "isSourcePresent = true"; break; default: // ouch throw new RuntimeException("Invalid switch statement: missing enum cases " + suffix); } query.append(q); first = false; } return query.toString(); } public static List<ModuleVersion> completeVersionForModuleAndBackend(Module module, String version, QueryParams params) { String typeQuery = getBackendQuery("v.", params); if (version == null) version = ""; String binaryQuery = getBinaryQuery("v.", params.binaryMajor, params.binaryMinor); String select = "SELECT DISTINCT v FROM ModuleVersion v "; String where = "WHERE v.module = :module AND LOCATE(:version, v.version) = 1 AND (" + typeQuery + ")" + binaryQuery; if (isNotEmpty(params.memberName)) { select += "LEFT JOIN v.members as memb "; where += getMemberQuery("memb.", params); } String q = select + where + " ORDER BY v.version"; JPAQuery query = ModuleVersion.find(q); query.bind("module", module); query.bind("version", version); if (isNotEmpty(params.memberName)) { query.bind("memberName", params.memberName.toLowerCase()); } addBinaryQueryParameters(query, params.binaryMajor, params.binaryMinor); return query.fetch(RepoAPI.RESULT_LIMIT); } static String getMemberQuery(String prefix, QueryParams params) { String where = ""; if (isNotEmpty(params.memberName)) { if (params.memberSearchPackageOnly) { if (params.memberSearchExact) { where += "AND LOWER(" + prefix + "packageName) = :memberName "; } else { where += "AND LOCATE(:memberName, LOWER(" + prefix + "packageName)) <> 0 "; } } else { if (params.memberSearchExact) { where += "AND LOWER(CONCAT(" + prefix + "packageName, '::', " + prefix + "name)) = :memberName "; } else { where += "AND LOCATE(:memberName, LOWER(CONCAT(" + prefix + "packageName, '::', " + prefix + "name))) <> 0 "; } } } return where; } static void addBinaryQueryParameters(JPAQuery query, Integer binaryMajor, Integer binaryMinor) { // Note that we use query.query.setParameter here rather than query.bindParameter because the latter // has a bug that turns Integer instances into Long instances (WTF?) if (binaryMajor != null) query.query.setParameter("binaryMajor", binaryMajor); if (binaryMinor != null) query.query.setParameter("binaryMinor", binaryMinor); } static String getBinaryQuery(String prefix, Integer binaryMajor, Integer binaryMinor) { if (binaryMajor == null && binaryMinor == null) return ""; StringBuilder ret = new StringBuilder("AND (").append(prefix).append("isCarPresent = false AND ") .append(prefix).append("isJsPresent = false OR ("); // these only apply to ceylon modules if (binaryMajor != null) ret.append(prefix).append("jvmBinMajor = :binaryMajor"); if (binaryMinor != null) { if (binaryMajor != null) ret.append(" AND "); ret.append(prefix).append("jsBinMinor = :binaryMinor"); } ret.append(") OR ("); if (binaryMajor != null) ret.append(prefix).append("jsBinMajor = :binaryMajor"); if (binaryMinor != null) { if (binaryMajor != null) ret.append(" AND "); ret.append(prefix).append("jsBinMinor = :binaryMinor"); } ret.append("))"); return ret.toString(); } public boolean matchesBinaryVersion(Integer binaryMajor, Integer binaryMinor) { if (binaryMajor != null && jvmBinMajor != binaryMajor && jsBinMajor != binaryMajor) return false; if (binaryMinor != null && jvmBinMinor != binaryMinor && jsBinMinor != binaryMinor) return false; return true; } @Transient public Set<String> getPackages() { Set<String> ret = new HashSet<String>(); for (ModuleMember member : members) { ret.add(member.packageName); } return ret; } public boolean containsPackage(String packageName) { return getPackages().contains(packageName); } }