分享
 
 
 

A program for increasing vocabulary, written in JAVA. (Version 0.1)

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

This time, I will try to make it clear.

I have run it for 2 days. No problem.

But there are still lots of TODO.

The version now is 0.1

I will try to give it a name and write a detail readme about how to use it.

Of course need finish the TODO in the program.

/*

* Config.java

* Created on Sep 29, 2004

* TODO Write the log to file.

*/

package com.henry.vocabulary;

import java.io.*;

import java.util.logging.*;

import java.util.*;

/**

* @author henry.pan

* TODO Add unit test for this class

*/

public class Config {

private static Logger logger = Logger.getLogger("Config");

private static String configFile = "config.ini";

/* here is the config and the default value */

private static String wordsFileName = "words.txt";

private static int sleepInterval = 6000 * 2;

/**

* @return The config of the file storing the words.

*/

public static String getWordsFileName() {

return wordsFileName;

}

public static int getSleepInterval() {

return sleepInterval;

}

/**

* Read configuration from config file.

* Config file is defined.

*/

public static void getTheConfiguration() {

File file = new File(configFile);

if (!file.exists()) {

logger.severe("The config file does not exist!" + Calendar.getInstance().getTime());

}

try {

StringBuffer sb = new StringBuffer();

BufferedReader br = new BufferedReader(new FileReader(configFile));

String s;

String[] configValues;

while ((s = br.readLine()) != null) {

if (s.equals("")) {

continue;

}

if (s.substring(0,0).equals("#")) {

continue;

}

configValues = s.split("=");

parseConfigValue(configValues);

}

} catch (IOException ioe) {

logger.severe("Error when reading confi file!");

logger.severe(ioe.getStackTrace().toString());

logger.severe(ioe.toString());

}

}

/**

* @param configValues The config paire values.

* configValues[0] is the config name

* configValues[1] is the config value

*/

private static void parseConfigValue(String[] configValues) {

assert configValues.length > 1 : "The config format is incorrect!";

try {

if (configValues[0].trim().toLowerCase() == "WordsFile") {

wordsFileName = configValues[1].trim();

}

if (configValues[0].trim().toLowerCase() == "SleepInterval") {

sleepInterval = Integer.parseInt( configValues[1].trim());

}

} catch (Exception e) {

logger.severe("The configuration's format is incorrect!");

logger.severe(e.getStackTrace().toString());

logger.severe(e.toString());

}

}

}

/*

* MainFrame.java

* Created on Sep 28, 2004

*

*/

package com.henry.vocabulary;

import java.awt.*;

import java.awt.event.*;

import java.util.Calendar;

import java.util.logging.*;

import javax.swing.*;

/*******************************************************************************

* Title:

*

*

* Description:

*

*

* Copyright: Copyright (c) 2004

*

*

* Company:

*

* @author Henry *

* @version 1.0

* TODO Add unit test for this class

*/

public class MainFrame extends JFrame {

Logger logger = Logger.getLogger("MainWin");

/* Used to manipulate the words */

WordsReview wr = new WordsReview();

JPanel contentPane;

GridBagLayout gridBagLayout1 = new GridBagLayout();

JTextField word = new JTextField();

JRadioButton remember = new JRadioButton();

JRadioButton forget = new JRadioButton();

JButton next = new JButton();

ButtonGroup buttonGroup1 = new ButtonGroup(); /* Construct the frame */

public MainFrame() {

enableEvents(AWTEvent.WINDOW_EVENT_MASK);

try {

jbInit();

wordsInit();

} catch (Exception e) {

e.printStackTrace();

}

}

/* Component initialization */

private void jbInit() throws Exception {

contentPane = (JPanel) this.getContentPane();

word.setFont(new java.awt.Font("Dialog", 0, 16));

word.setSelectionStart(0);

word.setText("");

contentPane.setLayout(gridBagLayout1);

this.setSize(new Dimension(600, 300));

this.setTitle("TAFU Vocabulary");

remember.setFont(new java.awt.Font("Dialog", 0, 16));

remember.setText("I remember it well.");

forget.setFont(new java.awt.Font("Dialog", 0, 16));

remember.setSelected(true);

forget.setText("I forget it.");

next.setFont(new java.awt.Font("Dialog", 0, 16));

next.setText("Next");

next.addActionListener(new Frame1_next_actionAdapter(this));

contentPane.add(word, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0,

GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(

17, 9, 0, 31), 494, 39));

contentPane.add(remember, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,

GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,

new Insets(26, 0, 1, 0), 0, 0));

contentPane.add(forget, new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0,

GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE,

new Insets(25, 0, 37, 0), 0, 0));

contentPane.add(next, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0,

GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(

0, 9, 59, 13), 44, 11));

buttonGroup1.add(forget);

buttonGroup1.add(remember);

}

/* Overridden so we can exit when window is closed */

protected void processWindowEvent(WindowEvent e) {

super.processWindowEvent(e);

if (e.getID() == WindowEvent.WINDOW_CLOSING) {

logger.info("Close the window!");

try {

wr.saveToFile();

} catch (Exception ex) {

System.out.println(ex.getStackTrace());

System.out.println(ex);

}

System.exit(0);

}

}

private void wordsInit() {

try {

wr.readFromFile();

getNextWord();

} catch (Exception e) {

System.out.println(e.getStackTrace());

System.out.println(e);

}

}

private void getNextWord() {

String s = this.wr.getWord();

if (s == null) {

this.setVisible(false);

this.wr.saveToFile();

while (s == null) {

logger.info("Get a null word. No word needs review now. Goto Sleep." + Calendar.getInstance().getTime());

try {

Thread.sleep(Config.getSleepInterval());

logger.info(" -- After sleep --" + Calendar.getInstance().getTime());

this.wr.readFromFile();

s = this.wr.getWord();

} catch (InterruptedException ie) {

System.out.println(ie.getStackTrace());

System.out.println(ie);

}

}

this.setVisible(true);

}

this.word.setText(s);

this.remember.setSelected(true);

}

void next_actionPerformed(ActionEvent e) {

if (this.forget.isSelected()) {

wr.saveWord(false);

} else {

wr.saveWord(true);

}

getNextWord();

}

}

class Frame1_next_actionAdapter implements java.awt.event.ActionListener {

MainFrame adaptee;

Frame1_next_actionAdapter(MainFrame adaptee) {

this.adaptee = adaptee;

}

public void actionPerformed(ActionEvent e) {

adaptee.next_actionPerformed(e);

}

}

/*

* MainWin.java

* Created on Sep 28, 2004

*/

package com.henry.vocabulary;

import javax.swing.UIManager;

import java.awt.*;

/*******************************************************************************

* Title:

*

*

* Description:

*

*

* Copyright: Copyright (c) 2004

*

*

* Company:

*

* @author not attributable *

* @version 1.0

* TODO Make sure there is only one instance running.

*/

public class MainWin {

boolean packFrame = false;

/* Construct the application */

public MainWin() {

MainFrame frame = new MainFrame();

/*

* Validate frames that have preset sizes

* Pack frames that have useful preferred size info, e.g. from their

* layout

*/

if (packFrame) {

frame.pack();

} else {

frame.validate();

}

/* Center the window */

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

Dimension frameSize = frame.getSize();

if (frameSize.height > screenSize.height) {

frameSize.height = screenSize.height;

}

if (frameSize.width > screenSize.width) {

frameSize.width = screenSize.width;

}

frame.setLocation((screenSize.width - frameSize.width) / 2,

(screenSize.height - frameSize.height) / 2);

frame.setVisible(true);

}

/* Main method */

public static void main(String[] args) {

boolean assertionsEnabled = false;

/* Note intentioal side effect of assignment. */

assert assertionsEnabled = true;

if (!assertionsEnabled) {

throw new RuntimeException("Assertions disabled");

}

try {

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

} catch (Exception e) {

e.printStackTrace();

}

new MainWin();

System.out.println("Exit the MainWin.main().");

}

}

/*******************************************************************************

* Word.java

*

* Created on 2004-9-26

*/

package com.henry.vocabulary;

import java.util.*;

import java.text.*;

public class Word {

private static final SimpleDateFormat format = new SimpleDateFormat(

"yyyy-MM-dd HH:mm:ss");

private static final int[] memoryCurveMinute = {10, 30, 60, 180, 300, 500};

private static final int[] memoryCurveDay = { 1, 2, 4, 8, 12, 20, 36, 54, 87,

160, 360 ,680};

private String _wordValue;

private Date _nextReviewTime;

private int _reviewTimes;

private int _forgetTimes;

/**

* *

*

* @param _wordValue

* The _wordValue to set.

*/

public void setWordValue(String _wordValue) {

this._wordValue = _wordValue;

}

/**

* *

*

* @return Returns the _wordValue.

*/

public String getWordValue() {

return _wordValue;

}

public Word(String wordValue, String nextReviewTime, int reviewTimes,

int forgetTimes) {

try {

setWordValue(wordValue);

_nextReviewTime = format.parse(nextReviewTime);

_reviewTimes = reviewTimes;

_forgetTimes = forgetTimes;

} catch (ParseException pe) {

System.out.println("The input is not a date!");

throw new RuntimeException(pe);

}

}

public Word(String strWord) {

try {

String[] values = strWord.split(",");

setWordValue(values[0]);

if(values.length > 2) {

_nextReviewTime = format.parse(values[1]);

_reviewTimes = Integer.parseInt(values[2].trim());

} else {

Calendar yesterday = Calendar.getInstance();

yesterday.add(Calendar.DATE, -1);

_nextReviewTime = yesterday.getTime();

_reviewTimes = 0;

}

if (values.length > 3) {

/* For compatible to the old version */

_forgetTimes = Integer.parseInt(values[3]);

} else {

_forgetTimes = 0;

}

} catch (ParseException pe) {

System.out.println("The input is not a date!");

throw new RuntimeException(pe);

}

}

public void forget() {

Calendar nextTime = Calendar.getInstance();

nextTime.add(Calendar.MINUTE, -1);

_nextReviewTime = nextTime.getTime();

_reviewTimes = 0;

_forgetTimes++;

}

public void remember() {

Calendar nextTime = Calendar.getInstance();

if (this._reviewTimes < memoryCurveMinute.length) {

nextTime.add(Calendar.MINUTE, memoryCurveMinute[_reviewTimes]);

} else if (_reviewTimes - memoryCurveMinute.length < memoryCurveDay.length) {

nextTime.add(Calendar.DATE, memoryCurveDay[_reviewTimes - memoryCurveMinute.length]);

} else {

nextTime.add(Calendar.YEAR, 100); /* Never need review this word */

}

if(nextTime.get(Calendar.DATE) != Calendar.getInstance().get(Calendar.DATE)) {

nextTime.set(Calendar.HOUR, 0);

nextTime.set(Calendar.MINUTE, 0);

nextTime.set(Calendar.SECOND, 0);

}

_nextReviewTime = nextTime.getTime();

_reviewTimes++;

}

public String toString() {

String s = getWordValue() + "," + format.format(_nextReviewTime) + ","

+ _reviewTimes + "," + _forgetTimes;

return s;

}

/**

* Judge if this word need to be reviewed now. * Comment for isNeedReview

*/

public boolean isNeedReview() {

if (this._nextReviewTime.after(Calendar.getInstance().getTime())) {

return false;

}

return true;

}

public boolean equals (Object o) {

if (this._wordValue.equalsIgnoreCase(((Word)o).getWordValue())) {

return true;

}

return false;

}

public boolean isNeedNotReviewAnyMore() {

Calendar later50Years = Calendar.getInstance();

later50Years.add(Calendar.YEAR, 50);

if (this._nextReviewTime.after(later50Years.getTime())) {

return true;

}

return false;

}

}

/*

* WordsReview.java

*

* Created on 2004-9-26

*

*

*/

package com.henry.vocabulary;

import java.util.*;

import java.util.logging.*;

import java.io.*;

/**

* TODO Add unit test for this class

*

* @author Henry *

*/

public class WordsReview {

static Logger logger = Logger.getLogger("WordsReview");

List wordList = null;

List wordNeedReviewList = null;

Word currentWord = null;

Map wordMap = null;

/**

* *

*

* @param fileName

*/

private void readFromFile(String fileName) {

assert (this.wordList != null) : "Doesnt init the wordList when readFromFile.";

createWordsFileIfItNotExist();

StringBuffer sb = null;

BufferedReader br = null;

try {

sb = new StringBuffer();

br = new BufferedReader(new FileReader(fileName));

String s;

while ((s = br.readLine()) != null) {

this.currentWord = new Word(s);

if (!this.wordMap.containsKey(this.currentWord.getWordValue())) {

this.wordMap.put(this.currentWord.getWordValue(),

this.currentWord);

this.wordList.add(this.currentWord);

if (this.currentWord.isNeedReview()) {

this.wordNeedReviewList.add(this.currentWord);

}

} else {

this.currentWord = (Word) this.wordMap.get(this.currentWord

.getWordValue());

this.currentWord.forget();

}

}

this.currentWord = null;

br.close();

} catch (FileNotFoundException e) {

System.out.println(e);

System.out.println(e.getStackTrace());

} catch (IOException ioe) {

System.out.println(ioe);

System.out.println(ioe.getStackTrace());

} finally {

/* br.close(); */

}

}

/*

* TODO After create the words file. Add the advertise words in the file.

* Such as http://blog.csdn.net/tafu

*

* TODO Put this function out of here.

*/

private void createWordsFileIfItNotExist() {

File f = new File(Config.getWordsFileName());

if (!f.exists()) {

try {

f.createNewFile();

} catch (IOException ioe) {

logger.severe("Cannot create file to save the words!"

+ Calendar.getInstance().getTime());

logger.severe(ioe.getStackTrace().toString());

logger.severe(ioe.toString());

}

}

}

/**

* *

*

* @param fileName

*/

private void saveToFile(String fileName) {

try {

PrintWriter printWriter = new PrintWriter(new BufferedWriter(

new FileWriter(fileName)));

Iterator iterator = this.wordList.iterator();

while (iterator.hasNext()) {

printWriter.println((Word) iterator.next());

}

printWriter.close();

} catch (IOException ioe) {

System.out.println(ioe.getStackTrace());

System.out.println(ioe);

}

}

public void saveToFile() {

this.saveToFile(Config.getWordsFileName());

}

/**

* * Get a word, which needs to be reviewed. *

*

* @return

*/

public String getWord() {

if (this.wordNeedReviewList.isEmpty()) {

return null;

}

Random random = new Random();

int ramdonIndex = random.nextInt() % this.wordNeedReviewList.size();

if (ramdonIndex < 0) {

ramdonIndex = -ramdonIndex;

}

currentWord = (Word) this.wordNeedReviewList.get(ramdonIndex);

return currentWord.getWordValue();

}

public void saveWord(boolean isRemember) {

if (this.currentWord == null) {

System.out.println("Should not come here!");

System.out.println("When you call saveWord to save the word, ");

System.out.println(" you should get the word first.");

return;

}

if (!isRemember) {

this.currentWord.forget();

} else {

this.currentWord.remember();

}

this.wordNeedReviewList.remove(this.currentWord);

if (this.currentWord.isNeedNotReviewAnyMore()) {

this.wordList.remove(this.currentWord);

this.wordMap.remove(this.currentWord.getWordValue());

}

this.currentWord = null;

}

public void regenerateNeedReviewList() {

this.wordNeedReviewList = new ArrayList();

for (int i = 0; i < this.wordList.size(); i++) {

if (((Word) this.wordList.get(i)).isNeedReview()) {

this.wordNeedReviewList.add((Word) this.wordList.get(i));

}

}

this.currentWord = null;

}

public void readFromFile() {

this.wordList = new ArrayList();

this.wordNeedReviewList = new LinkedList();

this.wordMap = new HashMap();

this.readFromFile(Config.getWordsFileName());

}

}

/*

* WordTest.java

* Created on Sep 30, 2004

*

*/

package com.henry.vocabulary.junit;

import junit.framework.TestCase;

import com.henry.vocabulary.*;

import java.util.*;

import java.text.*;

/**

* @author henry.pan

*

*/

public class WordTest extends TestCase {

/*

* @see TestCase#setUp()

*/

protected void setUp() throws Exception {

super.setUp();

}

/*

* @see TestCase#tearDown()

*/

protected void tearDown() throws Exception {

super.tearDown();

}

public void testSetWordValue() {

Word w = new Word("testWord");

w.setWordValue("newValue");

TestCase.assertEquals("newValue",w.getWordValue());

w = null;

}

public void testGetWordValue() {

Word w = new Word("testWord");

TestCase.assertEquals("testWord",w.getWordValue());

w = null;

}

public void testForget() {

Word w = new Word("testWord");

Calendar calendar = Calendar.getInstance();

calendar.add(Calendar.MINUTE, -1);

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:");

String s = df.format(calendar.getTime());

w.forget();

/* System.out.println(w); */

/* System.out.println("testWord," + s + "\d\d,0,\d+"); */

TestCase.assertTrue(w.toString().matches("testWord," + s + "\d\d,0,\d+"));

w.forget();

w.remember();

w.remember();

w.forget();

TestCase.assertTrue(w.toString().matches("testWord," + s + "\d\d,0,\d+"));

w = null;

}

public void testRemember() {

}

public void testIsNeedReview() {

Calendar calendar = Calendar.getInstance();

calendar.add(Calendar.MINUTE, -1);

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

String s = df.format(calendar.getTime());

Word w = new Word("testWord," + s + ",3,2");

TestCase.assertTrue(w.isNeedReview());

w = null;

}

/*

* Class under test for boolean equals(Object)

*/

public void testEqualsObject() {

Word w1 = new Word("testWord");

Word w2 = new Word("testWord");

w1.forget();

w2.remember();

TestCase.assertTrue(w1.equals(w2));

}

public void testIsNeedNotReviewAnyMore() {

Calendar calendar = Calendar.getInstance();

calendar.add(Calendar.YEAR, 100);

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

String s = df.format(calendar.getTime());

Word w = new Word("testWord," + s + "3,2");

TestCase.assertTrue(w.isNeedNotReviewAnyMore());

w = null;

}

}

//==========================================

And the test data in words.txt file.

distract,2004-10-01 00:00:00,7,0

marvel,2004-10-01 00:00:00,7,0

vaunt,2004-10-01 00:00:00,7,3

syllabus,2004-10-01 00:00:00,7,0

typo,2004-10-01 00:00:00,7,0

parallel,2004-10-01 00:00:00,7,0

paradigm,2004-10-01 00:00:00,7,0

mediocre,2004-10-01 00:00:00,7,2

stuck,2004-10-01 00:00:00,7,0

auxiliary,2004-10-01 00:00:00,7,1

somehow,2004-10-01 00:00:00,7,1

even number,2004-10-01 00:00:00,7,0

lore,2004-10-01 00:00:00,7,1

compel,2004-10-01 00:00:00,7,1

inclination,2004-10-01 00:00:00,7,1

garble,2004-10-01 00:00:00,7,1

innate,2004-10-01 00:00:00,7,0

imperative,2004-10-01 00:00:00,7,0

even up,2004-10-01 00:00:00,7,1

preemptive,2004-10-01 00:00:00,7,0

fathom,2004-10-01 00:00:00,7,0

moon cake,2004-10-01 00:00:00,7,0

chug,2004-10-01 00:00:00,7,0

unnecessary,2004-10-01 00:00:00,7,0

state,2004-10-01 00:00:00,7,0

largely,2004-10-01 00:00:00,7,0

elaborate,2004-10-01 00:00:00,7,0

gliph,2004-10-01 00:00:00,7,0

pug,2004-10-01 00:00:00,7,0

recipe,2004-10-01 00:00:00,7,0

adjective,2004-10-01 00:00:00,7,0

spam,2004-10-01 00:00:00,7,0

loaf,2004-10-01 00:00:00,7,0

circus,2004-10-01 00:00:00,7,0

incessantly,2004-10-01 00:00:00,7,1

cannibalize,2004-10-01 00:00:00,7,0

eligible,2004-10-01 00:00:00,7,0

parenthetic,2004-10-01 00:00:00,7,1

cue,2004-10-01 00:00:00,7,0

rejuvenate,2004-10-01 12:00:00,7,0

back away,2004-10-01 12:00:00,7,0

ballistic,2004-10-01 12:00:00,7,0

interrogate,2004-10-01 12:00:00,7,0

introspect,2004-10-01 12:00:00,7,0

plop,2004-10-01 12:00:00,7,0

self-aware,2004-10-01 12:00:00,7,0

deduction,2004-09-30 21:48:56,6,0

reimbursement,2004-09-30 21:51:09,6,0

relic,2004-09-30 21:51:13,6,0

unilateral,2004-09-30 21:53:23,6,0

loiter,2004-09-30 21:53:18,6,0

symptom,2004-09-30 18:39:04,5,0

conspire,2004-09-30 21:38:59,5,0

incubation,2004-09-30 21:41:03,5,0

ridiculous,2004-09-30 17:43:11,4,0

overextended,2004-09-30 20:13:37,4,0

summit,2004-09-30 20:15:39,4,1

endorse,2004-09-30 20:15:56,4,1

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有