2021SC@SDUSC 软件工程应用与实践——OpenMeetings项目分析(八)

2021SC@SDUSC

软件工程应用与实践——OpenMeetings项目分析(八):

本周,继续分析common包下tree包的内容。

FileTreePanel:

public abstract class FileTreePanel extends Panel {
	private static final long serialVersionUID = 1L;
	private static final String ALIGN_LEFT_CLASS = " align-left";
	private static final String ALIGN_RIGHT_CLASS = " align-right";
	private static final String BASE_CLASS = " om-icon big clickable";
	private static final String UPLOAD_CLASS = "add" + BASE_CLASS + ALIGN_LEFT_CLASS;
	private static final String CREATE_DIR_CLASS = "folder-create" + BASE_CLASS + ALIGN_LEFT_CLASS;
	private static final String TRASH_CLASS = "trash" + BASE_CLASS + ALIGN_RIGHT_CLASS;
	private static final String DISABLED_CLASS = " disabled";
	final WebMarkupContainer trees = new WebMarkupContainer("tree-container");
	private final WebMarkupContainer sizes = new WebMarkupContainer("sizes");
	private BaseFileItem lastSelected = null;
	private Map<String, BaseFileItem> selected = new HashMap<>();
	File dwnldFile;
	final AjaxDownloadBehavior downloader = new AjaxDownloadBehavior(new IResource() {
		private static final long serialVersionUID = 1L;

		@Override
		public void respond(Attributes attributes) {
			new FileSystemResource(dwnldFile.toPath()) {
				private static final long serialVersionUID = 1L;

				@Override
				protected ResourceResponse createResourceResponse(Attributes attr, Path path) {
					ResourceResponse response = super.createResourceResponse(attr, path);
					response.setCacheDuration(NONE);
					return response;
				}
			}.respond(attributes);
		}
	});
	protected final IModel<String> homeSize = Model.of((String)null);
	protected final IModel<String> publicSize = Model.of((String)null);
	final ConvertingErrorsDialog errorsDialog = new ConvertingErrorsDialog("errors", Model.of((Recording)null));
	final FileItemTree tree;
	final AjaxSplitButton download = new AjaxSplitButton("download", new ArrayList<IMenuItem>()) {
		private static final long serialVersionUID = 1L;

		@Override
		protected void onSubmit(AjaxRequestTarget target, IMenuItem item) {
			item.onClick(target);
		}
	};
	private final Form<Void> form = new Form<>("form");
	private final NameDialog addFolder;
	private final ConfirmableBorderDialog trashConfirm;
	private ConfirmableAjaxBorder trashBorder;
	private final Long roomId;
	private boolean readOnly = true;
	private final Component createDir = new WebMarkupContainer("create").add(new AjaxEventBehavior(EVT_CLICK) {
		private static final long serialVersionUID = 1L;

		@Override
		protected void onEvent(AjaxRequestTarget target) {
			addFolder.open(target);
		}
	});
	private final Component upload = new WebMarkupContainer("upload");

	public FileTreePanel(String id, Long roomId, NameDialog addFolder, ConfirmableBorderDialog trashConfirm) {
		super(id);
		this.roomId = roomId;
		this.addFolder = addFolder;
		this.trashConfirm = trashConfirm;
		final OmTreeProvider tp = new OmTreeProvider(roomId);
		select(tp.getRoot(), null, false, false);
		form.add(tree = new FileItemTree("tree", this, tp));
		form.add(download.setVisible(false).setOutputMarkupPlaceholderTag(true));
		add(form.add(downloader));
	}

	@Override
	protected void onInitialize() {
		super.onInitialize();
		download.setDefaultModelObject(newDownloadMenuList());
		Droppable<BaseFileItem> trashToolbar = new Droppable<BaseFileItem>("trash-toolbar") {
			private static final long serialVersionUID = 1L;

			@Override
			public void onConfigure(JQueryBehavior behavior) {
				super.onConfigure(behavior);
				behavior.setOption("hoverClass", Options.asString("ui-state-hover trash-toolbar-hover"));
				behavior.setOption("accept", Options.asString(".recorditem, .fileitem"));
			}

			@Override
			public JQueryBehavior newWidgetBehavior(String selector) {
				return new DroppableBehavior(selector, this) {
					private static final long serialVersionUID = 1L;

					@Override
					protected JQueryAjaxBehavior newOnDropAjaxBehavior(IJQueryAjaxAware source) {
						return new OnDropAjaxBehavior(source) {
							private static final long serialVersionUID = 1L;

							@Override
							public CharSequence getCallbackFunctionBody(CallbackParameter... parameters) {
								String dialogId = UUID.randomUUID().toString();

								String statement = "var $drop = $(this);";
								statement += "$('body').append('<div id=" + dialogId + ">" + getString("713") + "</div>');";
								statement += "$( '#" + dialogId
										+ "' ).dialog({ title: '" + escapeEcmaScript(getString("80")) + "', classes: {'ui-dialog-titlebar': 'ui-corner-all no-close'}, buttons: [";
								statement += "	{ text: '" + escapeEcmaScript(getString("54")) + "', click: function() { $drop.append(ui.draggable); $(this).dialog('close'); " + super.getCallbackFunctionBody(parameters) + " } },";
								statement += "	{ text: '" + escapeEcmaScript(getString("lbl.cancel")) + "', click: function() { $(this).dialog('close'); } } ";
								statement += "],";
								statement += "close: function(event, ui) { $(this).dialog('destroy').remove(); }";
								statement += "});";

								return statement;
							}
						};
					}
				};
			}

			@Override
			public void onDrop(AjaxRequestTarget target, Component component) {
				Object o = component.getDefaultModelObject();
				if (o instanceof BaseFileItem) {
					BaseFileItem f = (BaseFileItem)o;
					if (isSelected(f)) {
						deleteAll(target);
					} else {
						delete(f, target);
					}
				}
			}
		};
		form.add(trashToolbar);
		trashToolbar.add(getUpload());
		trashToolbar.add(createDir);
		trashToolbar.add(new WebMarkupContainer("refresh").add(new AjaxEventBehavior(EVT_CLICK) {
			private static final long serialVersionUID = 1L;

			@Override
			protected void onEvent(AjaxRequestTarget target) {
				update(target);
			}
		}));
		trashToolbar.add(trashBorder = new ConfirmableAjaxBorder("trash", getString("80"), getString("713"), trashConfirm) {
			private static final long serialVersionUID = 1L;

			@Override
			protected boolean isClickable() {
				return !readOnly && !selected.isEmpty();
			}

			@Override
			protected void onSubmit(AjaxRequestTarget target) {
				deleteAll(target);
			}
		});

		form.add(trees.add(tree).setOutputMarkupId(true));
		updateSizes();
		form.add(sizes.add(new Label("homeSize", homeSize), new Label("publicSize", publicSize)).setOutputMarkupId(true));
		form.add(errorsDialog);
		setReadOnly(false, null);
	}

	@Override
	public void renderHead(IHeaderResponse response) {
		super.renderHead(response);
		response.render(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(FileTreePanel.class, "filetree.js")));
	}

	protected String getContainment() {
		return ".file.item.drop.area";
	}

	protected Component getUpload() {
		return upload.setVisible(false);
	}

	private void deleteAll(AjaxRequestTarget target) {
		for (Entry<String, BaseFileItem> e : selected.entrySet()) {
			BaseFileItem f = e.getValue();
			if (!f.isReadOnly()) {
				delete(f, target);
			}
		}
		updateSelected(target);
		selected.clear();
	}

	void delete(BaseFileItem f, IPartialPageRequestHandler handler) {
		Long id = f.getId();
		if (id != null) {
			if (f instanceof Recording) {
				getBean(RecordingDao.class).delete(f);
			} else {
				getBean(FileItemDao.class).delete(f);
			}
		}
		update(handler);
	}

	public void setReadOnly(boolean readOnly, IPartialPageRequestHandler handler) {
		if (this.readOnly != readOnly) {
			this.readOnly = readOnly;
			tree.refreshRoots(!readOnly);
			createDir.setEnabled(!readOnly);
			createDir.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(CREATE_DIR_CLASS).append(readOnly ? DISABLED_CLASS : "")));
			upload.setEnabled(!readOnly);
			upload.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(UPLOAD_CLASS).append(readOnly ? DISABLED_CLASS : "")));
			trashBorder.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(TRASH_CLASS).append(readOnly ? DISABLED_CLASS : "")));
			if (handler != null) {
				handler.add(createDir, upload, trashBorder);
				update(handler);
			}
		}
	}

	public boolean isReadOnly() {
		return readOnly;
	}

	protected abstract void update(AjaxRequestTarget target, BaseFileItem f);

	public void createFolder(AjaxRequestTarget target, String name) {
		BaseFileItem p = lastSelected;
		boolean isRecording = p instanceof Recording;
		BaseFileItem f = isRecording ? new Recording() : new FileItem();
		f.setName(name);
		f.setHash(UUID.randomUUID().toString());
		f.setInsertedBy(getUserId());
		f.setInserted(new Date());
		f.setType(Type.Folder);
		f.setOwnerId(p.getOwnerId());
		f.setGroupId(p.getGroupId());
		f.setRoomId(p.getRoomId());
		f.setParentId(Type.Folder == p.getType() ? p.getId() : null);
		if (isRecording) {
			getBean(RecordingDao.class).update((Recording)f);
		} else {
			getBean(FileItemDao.class).update((FileItem)f);
		}
		update(target);
	}

	public abstract void updateSizes();

	public boolean isSelected(BaseFileItem f) {
		return selected.containsKey(f.getHash());
	}

	public Map<String, BaseFileItem> getSelected() {
		return selected;
	}

	public BaseFileItem getLastSelected() {
		return lastSelected;
	}

	public void update(IPartialPageRequestHandler handler) {
		updateSizes();
		handler.add(sizes, trees);
	}

	private void updateSelected(AjaxRequestTarget target) {
		for (Entry<String, BaseFileItem> e : selected.entrySet()) {
			updateNode(target, e.getValue());
		}
	}

	void updateNode(AjaxRequestTarget target, BaseFileItem fi) {
		if (fi != null && !fi.isDeleted() && target != null) {
			if (Type.Folder == fi.getType()) {
				tree.updateBranch(fi, target);
			} else {
				tree.updateNode(fi, target);
			}
		}
	}

	private static boolean sameParent(Long roomId, BaseFileItem f1, BaseFileItem f2) {
		if (f1 instanceof Recording && f2 instanceof FileItem) {
			return false;
		}
		if (f1.getParentId() != null && f1.getParentId().equals(f2.getParentId())) {
			return true;
		}
		if (f1.getParentId() == null && f2.getParentId() == null) {
			if (f1.getOwnerId() != null && f1.getOwnerId().equals(f2.getOwnerId())) {
				return true;
			}
			if (f1.getRoomId() != null && f1.getRoomId().equals(f2.getRoomId())) {
				return true;
			}
			if (f2 instanceof FileItem && roomId != null && f1.getRoomId() == null && f2.getRoomId() == null && f1.getOwnerId() == null && f2.getOwnerId() == null) {
				return true;
			}
		}
		return false;
	}

	private static boolean isDownloadable(BaseFileItem f) {
		return !f.isReadOnly() && (f.getType() == Type.Presentation || f.getType() == Type.Image);
	}

	public void select(BaseFileItem fi, AjaxRequestTarget target, boolean shift, boolean ctrl) {
		updateSelected(target); //all previously selected are in update list
		if (ctrl) {
			if (isSelected(fi)) {
				selected.remove(fi.getHash());
			} else {
				selected.put(fi.getHash(), fi);
			}
			lastSelected = fi;
		} else if (shift && lastSelected != null && !lastSelected.getHash().equals(fi.getHash()) && sameParent(roomId, fi, lastSelected)) {
			selected.clear();
			String lastHash = null;
			for (BaseFileItem f : tree.getProvider().getByParent(fi, fi.getParentId())) {
				if (lastHash == null) {
					if (f.getHash().equals(lastSelected.getHash())) {
						lastHash = fi.getHash();
					}
					if (f.getHash().equals(fi.getHash())) {
						lastHash = lastSelected.getHash();
					}
				}
				if (lastHash != null) {
					selected.put(f.getHash(), f);
					if (f.getHash().equals(lastHash)) {
						break;
					}
				}
			}
		} else {
			selected.clear();
			selected.put(fi.getHash(), fi);
			lastSelected = fi;
		}
		updateSelected(target); //all finally selected are in the update list
		if (target != null) {
			target.add(trashBorder, download.setVisible(isDownloadable(lastSelected)));
		}
	}

	@Override
	protected void onDetach() {
		homeSize.detach();
		publicSize.detach();
		super.onDetach();
	}

	private List<IMenuItem> newDownloadMenuList() {
		List<IMenuItem> list = new ArrayList<>();

		//original
		list.add(new DownloadMenuItem(getString("files.download.original"), this, null));
		//pdf
		list.add(new DownloadMenuItem(getString("files.download.pdf"), this, EXTENSION_PDF));
		//jpg
		list.add(new DownloadMenuItem(getString("files.download.jpg"), this, EXTENSION_JPG));
		return list;
	}
}

首先,上类的结构图:

image-20211119191210514

首先,此类继承自Panel类,本身就是一个图形化界面的组件面板;

构造方法:参数为String id, Long roomId, NameDialog addFolder, ConfirmableBorderDialog trashConfirm,没有返回值,作用是实例化FileTreePanle的对象;方法开头是利用super调用父组件Panel的参数为String的构造方法,然后将参数值赋值给类的成员变量,最后使用add方法为成员变量form添加根据传入参数创建的的FileItemTree等对象;

onInitialize方法:参数返回值都为空,作用是对组件进行初始化,首先利用super调用org.apache.wicket public abstract class Componen类的oninitiallize方法,然后创建了一个com.googlecode.wicket.jquery.ui.interaction.droppable public abstract class Droppable类型得对象,使用了内部类,重写了com.googlecode.wicket.jquery.core.JQueryContainer类的数个方法,其中重写newWidgetBehavior方法时,基返回对象部分又调用了内部类实例化OnDropAjaxBehavior对象,理解起来比较费劲,层层嵌套;方法最后同样是为成员变量form以及此方法中新创建的trashToolbar对象用add添加新属性或对象;

renderHead方法:形参为IHeaderResponse response返回值为空,作用是渲染组件的头部;首先用super调用了父类org.apache.wicket.Component的构造方法,然后创建一个JavaScriptResourceReference对象并作为forReference方法的参数,由reponse进行具体的渲染;

getContainment方法:非常简单,返回一个字符串常量;

getUpload方法:返回将WebMarkupContainer类型得成员变量upload进行setvisible方法后的component对象;

deleteAll方法:形参为AjaxRequestTarget target返回值为空,作用为删除全部文件面板,采用了iterator形式的for遍历了selected的entryset,最后对selected对象执行了clear方法,清除hashmap的键值对;

delete方法,形参为BaseFileItem f, IPartialPageRequestHandler handler,返回值为空,作用是删除特定文件项对应的面板;

setReadOnly方法:形参为boolean readOnly, IPartialPageRequestHandler handler,返回值为空,作用是使fileItemTree只读,判断给的参数和原有的状态参数相不相等,不相等就执行文件树的状态值的更新;

isReadOnly方法:返回是否为只读文件;

update方法:抽象方法留给子类去实现;createFolder方法:创建文件夹相关的面板操作,参数为AjaxRequestTarget target, String name,判断成员变量lastSelected的类型是否是Recording,是就新建f为Recording对象,否则新建为fileitem对象,然后对f进行一系列的属性复制赋值;

isSelected方法:形参为BaseFileItem f,返回值为空,作用为检查某文件是否被选中;

getSelected方法以及getLastSelected都是普通的get方法不做介绍;

updateSelected方法:形参为AjaxRequestTarget target,返回值为空,用以更新被选中的文件,代码时以iterator形式的for循环遍历selected的entrySet();

update作用类似,形参为IPartialPageRequestHandler handler,返回值为空,作用为更新面板状态,首先调用抽象函数(之后子类还会实现,目前为空)然后用handler的add方法将两个成员变量添加进去;

updateNode方法:形参为AjaxRequestTarget target, BaseFileItem fi,返回值为空,作用为更新成员变量文件树的结点,判断传入的basefileitem是否为空或者已经被删除,不为空且没被删除,则更新到文件树里;

sameParent方法:形参为:Long roomId, BaseFileItem f1, BaseFileItem f2,判断两个基础文件项是否为同一父文件项下的文件项;

select方法:形参为BaseFileItem fi, AjaxRequestTarget target, boolean shift, boolean ctrl,作用为选中某个文件,根据输入的两个布尔值,进行判断当前文件是否已经被选中,选中了就删除,没选中就选择上,这是在按着ctrl键的情况,如果没按ctrl,需要同时按着shift且lastSelected不为空且lastSelected和参数传入的基础文件项相同,采取更新文件项,将要选中的文件加入到选中队列里;

onDetach方法:作用为组件分离时执行的具体操作,用两个成员变量的detach方法具体执行;

newDownloadMenuList方法:创建动态数组,并向里面add新的DownloadMenuItem对象,三个种类的文件,分别是原始文件(文本),pdf和jpg图片;

OmTreeProvider:

/*
 * 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.web.common.tree;

import static org.apache.openmeetings.db.util.AuthLevelUtil.hasAdminLevel;
import static org.apache.openmeetings.web.app.Application.getBean;
import static org.apache.openmeetings.web.app.WebSession.getRights;
import static org.apache.openmeetings.web.app.WebSession.getUserId;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.openmeetings.db.dao.file.FileItemDao;
import org.apache.openmeetings.db.dao.record.RecordingDao;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.file.BaseFileItem;
import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
import org.apache.openmeetings.db.entity.file.FileItem;
import org.apache.openmeetings.db.entity.record.Recording;
import org.apache.openmeetings.db.entity.user.Group;
import org.apache.openmeetings.db.entity.user.GroupUser;
import org.apache.openmeetings.web.app.Application;
import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

public class OmTreeProvider implements ITreeProvider<BaseFileItem> {
	private static final long serialVersionUID = 1L;
	private static final List<Type> VIDEO_TYPES = Arrays.asList(Type.Folder, Type.Video);
	public static final String RECORDINGS_MY = "recordings-my";
	public static final String RECORDINGS_PUBLIC = "recordings-public";
	public static final String RECORDINGS_GROUP = "recordings-group-%s";
	public static final String FILES_MY = "files-my";
	public static final String FILES_ROOM = "files-room";
	public static final String FILES_GROUP = "files-group-%s";
	private final Long roomId;
	private final List<BaseFileItem> roots = new ArrayList<>();
	private final String PUBLIC, GROUP_FILE, GROUP_REC;

	public OmTreeProvider(Long roomId) {
		this.roomId = roomId;
		PUBLIC = Application.getString("861");
		GROUP_FILE = Application.getString("files.root.group");
		GROUP_REC = Application.getString("recordings.root.group");
		refreshRoots(true);
	}

	public void refreshRoots(boolean all) {
		List<BaseFileItem> fRoot = new ArrayList<>(), rRoot = new ArrayList<>();
		if (all && roomId != null) {
			BaseFileItem r = createRoot(Application.getString("706"), FILES_MY, false);
			r.setOwnerId(getUserId());
			fRoot.add(r);
		}
		if (roomId != null) {
			BaseFileItem r = createRoot(Application.getString("707"), FILES_ROOM, false);
			r.setRoomId(roomId);
			fRoot.add(r);
		}
		if (all) {
			{
				BaseFileItem r = createRoot(Application.getString("860"), RECORDINGS_MY, true);
				r.setOwnerId(getUserId());
				rRoot.add(r);
			}
			{
				BaseFileItem r = createRoot(PUBLIC, RECORDINGS_PUBLIC, true);
				rRoot.add(r);
			}
		}
		for (GroupUser gu : getBean(UserDao.class).get(getUserId()).getGroupUsers()) {
			Group g = gu.getGroup();
			boolean readOnly = g.isRestricted() && !hasAdminLevel(getRights()) && !gu.isModerator();
			if (all) {
				BaseFileItem r = createRoot(String.format("%s (%s)", GROUP_REC, g.getName()), String.format(RECORDINGS_GROUP, g.getId()), true);
				r.setReadOnly(readOnly);
				r.setGroupId(g.getId());
				rRoot.add(r);
			}
			BaseFileItem r = createRoot(String.format("%s (%s)", GROUP_FILE, g.getName()), String.format(FILES_GROUP, g.getId()), false);
			r.setGroupId(g.getId());
			//group videos are read-only in recordings tree
			r.setReadOnly(roomId == null || readOnly);
			fRoot.add(r);
		}
		roots.clear();
		if (roomId == null) {
			roots.addAll(rRoot);
			roots.addAll(fRoot);
		} else {
			roots.addAll(fRoot);
			roots.addAll(rRoot);
		}
	}

	static BaseFileItem createRoot(String name, String hash, boolean rec) {
		BaseFileItem f = rec ? new Recording() : new FileItem();
		f.setType(Type.Folder);
		f.setName(name);
		f.setHash(hash);
		return f;
	}

	public BaseFileItem getRoot() {
		return roots.get(0);
	}

	@Override
	public Iterator<BaseFileItem> getRoots() {
		return roots.iterator();
	}

	public List<BaseFileItem> getByParent(BaseFileItem node, Long id) {
		List<BaseFileItem> list = new ArrayList<>();
		if (node instanceof Recording) {
			Recording rec = (Recording)node;
			RecordingDao dao = getBean(RecordingDao.class);
			List<Recording> _list;
			if (id == null) {
				if (node.getOwnerId() == null) {
					_list = dao.getRootByPublic(rec.getGroupId());
				} else {
					_list = dao.getRootByOwner(node.getOwnerId());
				}
			} else {
				_list = dao.getByParent(id);
			}
			list.addAll(_list);
		} else {
			FileItemDao dao = getBean(FileItemDao.class);
			List<FileItem> _list;
			if (id == null) {
				if (node.getRoomId() != null) {
					_list = dao.getByRoom(node.getRoomId());
				} else if (node.getGroupId() != null) {
					_list = dao.getByGroup(node.getGroupId(), roomId == null ? VIDEO_TYPES : null);
				} else {
					_list = dao.getByOwner(node.getOwnerId());
				}
			} else {
				_list = dao.getByParent(id, roomId == null ? VIDEO_TYPES : null);
			}
			list.addAll(_list);
		}
		if (node.isReadOnly()) {
			for (BaseFileItem f : list) {
				f.setReadOnly(true);
			}
		}
		return list;
	}

	@Override
	public Iterator<BaseFileItem> getChildren(BaseFileItem node) {
		return getByParent(node, node.getId()).iterator();
	}

	@Override
	public boolean hasChildren(BaseFileItem node) {
		return node.getId() == null || Type.Folder == node.getType();
	}

	@Override
	public IModel<BaseFileItem> model(BaseFileItem object) {
		return Model.of(object);
	}

	@Override
	public void detach() {
		//no-op
	}
}

image-20211120005408343

此类实现了org.apache.wicket.extensions.markup.html.repeater.tree包下的ITreeProvider<T>的接口,提供一些关于文件树的操作的一些方法;

构造方法:形参为Long roomId,用来实例化OmTreeProvider对象,先对一系列成员变量进行赋值,然后刷新状态;

refreshRoots方法:形参为布尔值all,用来刷新状态,首先创建两个动态数组froot和rRoot,然后根据布尔值all以及roomid是否等于0来创建不同的新的基础文件项添加到数组内,并设置组号,房间号,是否只读等属性,最后再利用迭代器形式的for循环遍历GroupUser对象,更新root的状态,先clear再add方法前面创建的froot和rroot;createRoot方法:形参为String name, String hash, boolean rec,返回值为BaseFileItem,作用是根据指定的根文件的参数创建一个根文件;

getRoot方法:返回成员变量roots的key为0的value,参数为空,返回值为一个BaseFileItem的对象;

getRoots方法:返回roots对象的迭代器,返回值是一个basefileitem类型的迭代器对象;

getByParent方法:形参为BaseFileItem node, Long id,返回值为List<BaseFileItem>,作用为通过某文件树里的项的父文件项获取文件项本身,首先判断传入的参数node的类型是不是Recording类型,是就创建一个RecordingDao对象,否就创建一个FileItemDao对象,然后需要判断传入的id是否存在以及node.getOwnerId的id是否存在,然后根据判断结果采用不同的方法得到文件项并将它add到list后面,最后根据node是否为只读,选择性的将结果设置只读然后返回;

getChildren方法:形参aseFileItem node,得到某文件项的子文件项,返回值Iterator<BaseFileItem>;

hasChildren方法:形参为BaseFileItem node,返回值为boolean,判断某文件项是否含有子项;

model方法:返回值为IModel<BaseFileItem>,形参为BaseFileItem object,利用org.apache.wicket.model类的对象调用of方法获取BaseFileItem类型的IModel对象;

至此,common下的tree包下的java类分析完成,下面简单分析一下此目录下的html文件

html文件:

FolderPanel.html,FileTreePanel.html:
<html xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
	<div class="file item"><wicket:child /><span wicket:id="name" class="name"></span></div>
</wicket:panel>
</html>

<html xmlns:wicket="http://wicket.apache.org">
<wicket:extend><span wicket:id="errors" class="errors"></span></wicket:extend>
</html>

只是用来wicket包下的标签构建了一个简单的页面;

ConvertingErrorsDialog.html:
<html xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
	<div wicket:id="container" class="dialog errors">
		<h3 wicket:id="message"></h3>
		<table style="width: 100%">
			<tr>
				<th><wicket:message key="1593"/></th>
				<th><wicket:message key="1594"/></th>
			</tr>
			<tr wicket:id="row">
				<td wicket:id="exitCode"></td>
				<td wicket:id="message" class="message"></td>
			</tr>
		</table>
	</div>
</wicket:panel>
</html>

div标签里面套了一个三级标题和一个table表格。仅仅是对标签的属性进行了一些设置,没有实际的内容展示;FileItemPanel.html类似,不再赘述;

filetree.js:
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
function dragHelper() {
	let s = $(this);
	if (s.hasClass('ui-state-active')) {
		s = $('.file.ui-state-active.ui-draggable.ui-draggable-handle, .recording.ui-state-active.ui-draggable.ui-draggable-handle');
	}
	const c = $('<div/>').attr('id', 'draggingContainer').addClass('drag-container').width(80).height(36)
		, h = $('<div class="ui-corner-all ui-widget-header"/>').append(s.clone()).width(s.width());
	return c.append(h);
}
function treeRevert(dropped) {
	$('.file.tree .trees')[0].scrollTop = $(this).parent()[0].offsetTop - 32;
	return !dropped || (!!dropped && !!dropped.context && $(dropped.context).hasClass('wb', 'room'));
}

定义了两个函数drapHelper和treeRevert;

函数里主要是各类参数麻烦,结构并不困难;

至此,tree包下的类分析完毕。篇幅原因,本周不继续往下分析,下周应开始分析common包下剩余的类,包含数10个java类以及若干html文件,大部分都是图形界面有关的类,代码量仍然很大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HiddenWorld

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值